<!DOCTYPE html>



  


<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT" />










<meta name="description" content="开源 | Java | 个人笔记 | 我的博客">
<meta property="og:type" content="website">
<meta property="og:title" content="Hi , 小坤坤">
<meta property="og:url" content="http://smallkk.gitee.io/index.html">
<meta property="og:site_name" content="Hi , 小坤坤">
<meta property="og:description" content="开源 | Java | 个人笔记 | 我的博客">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Hi , 小坤坤">
<meta name="twitter:description" content="开源 | Java | 个人笔记 | 我的博客">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Pisces',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://smallkk.gitee.io/"/>





  <title>Hi , 小坤坤</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left 
  page-home">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Hi , 小坤坤</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
  <section id="posts" class="posts-expand">
    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/05/28/http/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/05/28/http/" itemprop="url">HTTP</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-28T00:48:00+08:00">
                2018-05-28
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="HTTP"><a href="#HTTP" class="headerlink" title="HTTP"></a>HTTP</h3><hr>
<p><img src="https://s3.didiyunapi.com/sxk/pic/http/1.png" alt="image"></p>
<ul>
<li>客户端–发送带有SYN标志的数据包–一次握手–服务端</li>
<li>服务端–发送带有SYN/ACK标志的数据包–二次握手–客户端</li>
<li>客户端–发送带有带有ACK标志的数据包–三次握手–服务端</li>
</ul>
<p><img src="https://s3.didiyunapi.com/sxk/pic/http/2.png" alt="image"></p>
<ul>
<li>客户端-发送一个FIN，用来关闭客户端到服务器的数据传送</li>
<li>服务器-收到这个FIN，它发回一个ACK，确认序号为收到的序号加1 。和SYN一样，一个FIN将占用一个序号</li>
<li>服务器-关闭与客户端的连接，发送一个FIN给客户端</li>
<li>客户端-发回ACK报文确认，并将确认序号设置为收到序号加1</li>
</ul>
<p><img src="https://s3.didiyunapi.com/sxk/pic/http/3.png" alt="image"></p>
<p>Http协议是建立在TCP协议基础之上的，当浏览器需要从服务器获取网页数据的时候，会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道，当本次请求需要的数据完毕后，Http会立即将TCP连接断开，这个过程是很短的。所以Http连接是一种短连接，是一种无状态的连接。</p>
<p><img src="https://s3.didiyunapi.com/sxk/pic/http/4.jpg" alt="image"></p>
<p>HTTP传输流:</p>
<p><img src="https://s3.didiyunapi.com/sxk/pic/nginx/5.jpg" alt="image"></p>
<ul>
<li>发送端在层与层间传输数据时，每经过一层都会被加上首部信息，接收端每经过一层都会删除一条首部</li>
</ul>
<p><img src="https://s3.didiyunapi.com/sxk/pic/nginx/tcp-ip.gif" alt="image"></p>
<ul>
<li><p>会话层以上分层</p>
<ul>
<li>TCP/IP 分层中，会话层，表示层，应用层集中在一起</li>
<li>网络管理通过 SNMP 协议</li>
</ul>
</li>
<li><p>传输层</p>
<ul>
<li>通用的 TCP 和 UDP 协议<ul>
<li>TCP 协议面向有连接，能正确处理丢包，传输顺序错乱的问题，但是为了建立与断开连接，需要至少7次的发包收包，资源浪费</li>
<li>UDP 面向无连接，不管对方有没有收到，如果要得到通知，需要通过应用层</li>
</ul>
</li>
</ul>
</li>
<li><p>网络层</p>
<ul>
<li>使用 IP 协议，IP 协议基于 IP 转发分包数据</li>
<li>IP 协议是个不可靠协议，不会重发</li>
<li>IP 协议发送失败会使用ICMP 协议通知失败</li>
<li>ARP 解析 IP 中的 MAC 地址，MAC 地址由网卡出厂提供</li>
<li>IP 还隐含链路层的功能，不管双方底层的链路层是啥，都能通信</li>
</ul>
</li>
<li><p>链路层代表驱动</p>
</li>
<li><p>物理层将二进制的0和1和电压高低，光的闪灭和电波的强弱信号进行转换</p>
</li>
</ul>
<h4 id="状态码"><a href="#状态码" class="headerlink" title="状态码"></a>状态码</h4><h6 id="2XX-成功"><a href="#2XX-成功" class="headerlink" title="2XX 成功"></a>2XX 成功</h6><ul>
<li>200 OK，表示从客户端发来的请求在服务器端被正确处理</li>
<li>204 No content，表示请求成功，但响应报文不含实体的主体部分</li>
<li>206 Partial Content，进行范围请求</li>
</ul>
<h6 id="3XX-重定向"><a href="#3XX-重定向" class="headerlink" title="3XX 重定向"></a>3XX 重定向</h6><ul>
<li>301 moved permanently，永久性重定向，表示资源已被分配了新的 URL</li>
<li>302 found，临时性重定向，表示资源临时被分配了新的 URL</li>
<li>303 see other，表示资源存在着另一个 URL，应使用 GET 方法丁香获取资源</li>
<li>304 not modified，表示服务器允许访问资源，但因发生请求未满足条件的情况</li>
<li>307 temporary redirect，临时重定向，和302含义相同</li>
</ul>
<h6 id="4XX-客户端错误"><a href="#4XX-客户端错误" class="headerlink" title="4XX 客户端错误"></a>4XX 客户端错误</h6><ul>
<li>400 bad request，请求报文存在语法错误</li>
<li>401 unauthorized，表示发送的请求需要有通过 HTTP 认证的认证信息</li>
<li>403 forbidden，表示对请求资源的访问被服务器拒绝</li>
<li>404 not found，表示在服务器上没有找到请求的资源</li>
<li>405 用来访问本页面的 HTTP 谓词不被允许（方法不被允许） </li>
<li>413 请求实体太大</li>
<li>414 请求 URI 太长</li>
</ul>
<h6 id="5XX-服务器错误"><a href="#5XX-服务器错误" class="headerlink" title="5XX 服务器错误"></a>5XX 服务器错误</h6><ul>
<li>500 internal sever error，表示服务器端在执行请求时发生了错误</li>
<li>503 service unavailable，表明服务器暂时处于超负载或正在停机维护，无法处理请求</li>
<li>505 HTTP版本不受支持</li>
</ul>
<h4 id="HTTP协议格式？"><a href="#HTTP协议格式？" class="headerlink" title="HTTP协议格式？"></a>HTTP协议格式？</h4><h6 id="HTTP的请求和响应的消息协议是一样的，分为三个部分，起始行、消息头和消息体。这三个部分以CRLF作为分隔符。最后一个消息头有两个CRLF，用来表示消息头部的结束。"><a href="#HTTP的请求和响应的消息协议是一样的，分为三个部分，起始行、消息头和消息体。这三个部分以CRLF作为分隔符。最后一个消息头有两个CRLF，用来表示消息头部的结束。" class="headerlink" title="HTTP的请求和响应的消息协议是一样的，分为三个部分，起始行、消息头和消息体。这三个部分以CRLF作为分隔符。最后一个消息头有两个CRLF，用来表示消息头部的结束。"></a>HTTP的请求和响应的消息协议是一样的，分为三个部分，起始行、消息头和消息体。这三个部分以CRLF作为分隔符。最后一个消息头有两个CRLF，用来表示消息头部的结束。</h6><p><img src="https://s3.didiyunapi.com/sxk/pic/nginx/6.jpg" alt="image"></p>
<ul>
<li>HTTP请求的起始行称为请求行，形如GET /index.html HTTP/1.1</li>
<li>HTTP响应的起始行称为状态行，形如200 ok</li>
<li>消息头部有很多键值对组成，多个键值对之间使用CRLF作为分隔符，也可以完全没有键值对<ul>
<li>形如Content-Encoding: gzip 消息体是一个字符串，字符串的长度是由消息头部的Content-Length键指定的</li>
<li>如果没有Content-Length字段说明没有消息体，譬如GET请求就是没有消息体的，POST请求的消息体一般用来放置表单数据</li>
<li>GET请求的响应返回的页面内容也是放在消息体里面的。我们平时调用API返回的JSON内容都是放在消息体里面的。</li>
</ul>
</li>
</ul>
<h5 id="输入url到页面加载都发生了什么事情？（最最常问的来了）"><a href="#输入url到页面加载都发生了什么事情？（最最常问的来了）" class="headerlink" title="输入url到页面加载都发生了什么事情？（最最常问的来了）"></a>输入url到页面加载都发生了什么事情？（最最常问的来了）</h5><ul>
<li>输入地址</li>
<li>浏览器查找域名的 IP 地址 这一步包括 DNS 具体的查找过程，包括：浏览器缓存-&gt;系统缓存-&gt;路由器缓存…</li>
<li>浏览器向 web 服务器发送一个 HTTP 请求</li>
<li>服务器的永久重定向响应（从 <a href="http://example.com" target="_blank" rel="noopener">http://example.com</a> 到 <a href="http://www.example.com）" target="_blank" rel="noopener">http://www.example.com）</a></li>
<li>浏览器跟踪重定向地址</li>
<li>服务器处理请求</li>
<li>服务器返回一个 HTTP 响应</li>
<li>浏览器显示 HTML</li>
<li>浏览器发送请求获取嵌入在 HTML 中的资源（如图片、音频、视频、CSS、JS等等）</li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/05/27/随笔/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/05/27/随笔/" itemprop="url">随笔</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-27T01:11:23+08:00">
                2018-05-27
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p><strong>1. AOP</strong><br>两种实现方式： </p>
<ul>
<li>① JDK  Proxy代理类和自定义类实现InvocationHandler的<code>invoke</code>,一起用,调用处理;使用JDK的话必须要实现接口;优势： 通过反射创建对象 创建销量高</li>
<li>②  cglib的asm搞字节码的class来搞这个切面编程的; 是基于子类，是基于继承;没有接口的时候，只能用这个;    优势： 执行效率高，但是创建不高</li>
<li>Spring默认用的是JDK方式,面向接口编程,动态代理,拦截有接口的方法</li>
<li>使用场景： 事务,同步,错误处理,日志等</li>
</ul>
<p><strong>1.5 IOC</strong></p>
<ul>
<li>IOC容器其实就是一个大工厂</li>
<li>原理就是通过Java的反射技术来实现的！通过反射我们可以获取类的所有信息(成员变量、类名等等等)！</li>
<li>Spring4.x开始IOC容器装配Bean有4种方式：<ul>
<li>XML配置</li>
<li>注解</li>
<li>JavaConfig</li>
<li>基于Groovy DSL配置(这种很少见)</li>
</ul>
</li>
<li>依赖注入的方式有3种方式：<ul>
<li>属性注入–&gt;通过setter()方法注入</li>
<li>构造函数注入</li>
<li>工厂方法注入</li>
</ul>
</li>
<li>Bean的作用域：<ul>
<li>单例Singleton</li>
<li>多例prototype</li>
<li>与Web应用环境相关的Bean作用域(request,session)<br><img src="https://s3.didiyunapi.com/sxk/pic/spring/ioc.jpg" alt="image"></li>
</ul>
</li>
</ul>
<p><img src="https://s3.didiyunapi.com/sxk/pic/spring/ioc2.jpg" alt="image"></p>
<p><img src="https://s3.didiyunapi.com/sxk/pic/spring/spring.jpg" alt="image"></p>
<ul>
<li><strong>使用Spring框架的好处是什么</strong>？<ul>
<li><strong>轻量</strong>：Spring 是轻量的，基本的版本大约2MB。</li>
<li><strong>控制反转</strong>：Spring通过控制反转实现了松散耦合，对象们给出它们的依赖，而不是创建或查找依赖的对象们。</li>
<li><strong>面向切面的编程(AOP)</strong>：Spring支持面向切面的编程，并且把应用业务逻辑和系统服务分开。</li>
<li>容器：Spring 包含并管理应用中对象的生命周期和配置。</li>
<li>MVC框架：Spring的WEB框架是个精心设计的框架，是Web框架的一个很好的替代品。</li>
<li>事务管理：Spring 提供一个持续的事务管理接口，可以扩展到上至本地事务下至全局事务（JTA）。</li>
<li>异常处理：Spring 提供方便的API把具体技术相关的异常（比如由JDBC，Hibernate or JDO抛出的）转化为一致的unchecked 异常。</li>
</ul>
</li>
<li>IOC的优点是什么？<ul>
<li>最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载服务时的饿汉式初始化和懒加载。</li>
</ul>
</li>
</ul>
<ol start="2">
<li><p>随机数的边界：<br>Random random = new Random();<br>return String.valueOf(random.nextInt(10000)); 表示生成的是一个10000以内的4位数</p>
</li>
<li><p>字符串的截取： return StringUtils.substring(String.valueOf(random.nextInt()), 2, 6);</p>
</li>
<li><p>SpringBoot的3种启动方式：<br>①IDEA启动<br>②在项目目录下进入命令行输入   mvn spring-boot:run<br>③项目目录下进入命令行输入 mvn install (先将项目编译,会生成target目录)<br>cd target     java -jar 项目名.jar<br>5.SpringBoot的这三种启动方式可以同时启动但是，要设置不同的启动端口；</p>
</li>
<li><p>@Value(直接注入参数)  @Component(针对不能注入的情况其实是可以注入的)<br> @ConfigurationProperties(prefix=”xxx”) 是获取配置文件中的配置;     dev / prod 多环境配置</p>
</li>
<li><p>@Controller 处理http请求 @RequestMapping 配置url映射<br> @RestController Spring4之后新加的注解,原来返回json需要@ResponseBody配合@Controller </p>
</li>
</ol>
<ol start="8">
<li><p>@PathVariable 获取url中的数据<br>eg: @re..ing(“/hello/{id}”) 方法中say(@PathVariable(“id”) Integer id)<br>传统的通过问号的方式传值：方法里面：say(@RequestParam(“id”) Integer myId) -&gt;  ?id=123<br>public String say(@RequestParam(value=”id”,required=false,defaultValue=”0”)Integer myId)<br>通过问号来传一个值，是否必传：否；默认值为“0” (此处不能为整型，只能为字符)</p>
</li>
<li><p>@RequestParam 获取请求参数的值</p>
</li>
<li><p>@GetMapping 组合注解      @GetMapping ==   @RequestMapping</p>
</li>
<li><p>JPA(JAVA Persistence API)定义了一系列对象持久化的标准，目前实现这一规范的产品还有                                 Hibernate、TopLink</p>
</li>
<li><p>SpringBoot建表规范：<br>JPA的配置：    </p>
</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">datasource:</span><br><span class="line">  driver-class-name: com.mysql.jdbc.Driver</span><br><span class="line">  url: jdbc:mysql://localhost/ooo</span><br><span class="line">  username: root</span><br><span class="line">  password: war3sxkcs</span><br><span class="line">jpa:</span><br><span class="line">  hibernate:</span><br><span class="line">    ddl-auto: create</span><br><span class="line">  show-sql: true</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">对象类：</span><br><span class="line">@Entity// 表示是实体</span><br><span class="line">    @Data</span><br><span class="line">public class Girl &#123;</span><br><span class="line">    @Id //表示这个是主键</span><br><span class="line">    @GeneratedValue// 表示是自动增长</span><br><span class="line">    private Integer id;</span><br><span class="line">    private String cupSize;</span><br><span class="line">    private Integer age;</span><br><span class="line">这个样子生成的SQL</span><br><span class="line">CREATE TABLE `girl` (</span><br><span class="line">  `id` int(11) NOT NULL AUTO_INCREMENT,</span><br><span class="line">  `age` int(11) DEFAULT NULL,</span><br><span class="line">  `cup_size` varchar(255) DEFAULT NULL,</span><br><span class="line">  PRIMARY KEY (`id`)</span><br><span class="line">  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;</span><br></pre></td></tr></table></figure>
<p>注意：在JPA的配置中   ddl-auto: create 表示创建这个表如果表里面有值的话会将原来的值先删除；如果是ddl-auto: update的话，就不会删除里面的内容</p>
<ol start="13">
<li><p>Linux命令行下载文件：  wget  https://……</p>
</li>
<li><p>jsp中的PrintWriter会先于out打印出来;不管这两个在代码里写的顺序;打印出来都是printwriter先;但是如果想让这两个按照在代码里面写的顺序打印出来的话就要使用out.flush强制释放输出.</p>
</li>
<li><p>重定向： response.sendRedirect(location) 服务器重新请求到一个新的地方</p>
</li>
<li><p>请求重定向：response.sendRedirect:客户端行为，从本质上讲等同于两次请求，前一次的请求对象不会保存，地址栏的URL地址会改变。请求转发request.getRequestDispatcher().forward(req,resp)是一次请求，转发后请求对象会保存，地址栏的URL地址不会改变</p>
</li>
<li><p>Session生命周期： 活动: 某次会话当中通过超链接打开的新特免属于同一会话；只有当前会话页面没有全部关闭，重新打开新的浏览器窗口访问同一项目资源的时候属于同一次会话。除非本次会话的所有页面都关闭后再重新访问某个JSP或者Servlet将会创建新的会话。(注意：原有的会话还存在，只是这个旧的的session仍然存在于服务端，只不过再也没有客户端会携带它然后交于服务端校验)</p>
</li>
<li><p>StringUtils.isNotBlank :(“  “)false  引号里面有无数个空格， 也表示这个值为空<br>StringUtils.isNotEmpty:(“  “)  true  引号里面有空格，表示这是个实值，有字符</p>
</li>
<li><p>@RequestParam(value = “categoryId”, defaultValue = “0”) Integer categoryId<br>动态传递参数通过URL的 “?” 、GET的请求方式</p>
</li>
<li><p>ArrayList和LinkList各有所长,综合两者优点,就用HashMap</p>
</li>
<li></li>
</ol>
<ul>
<li>Thread.yield();表示执行到这一行的时候,让出当前的执行权;</li>
<li>启动thread.start()方法后并不是立即就执行,而是进入了一种可执行的状态,等待是否可以执行</li>
<li>thread.join();表示执行到这儿的时候,thread线程开始执行,执行完了后再执行其他的线程</li>
<li>Thread.interrupt();中断线程标志位,但是并不会被中止</li>
<li>wait();是线程等待,需要notify();方法来唤醒;(涉及到两个对象：new Waiter();new Notifiter() )</li>
</ul>
<p>22.自定义异常类里面用到的注解：</p>
<ul>
<li>@ResponseBody    转化为JSON</li>
<li>@ControllerAdvice  对Controller层进行拦截处理</li>
<li>@ExceptionHandler(Exception.class)  捕获到这个(Controller层)类的异常</li>
</ul>
<ol start="23">
<li></li>
</ol>
<ul>
<li>synchronised;同步代码块; 锁 可以是任何一个对象</li>
<li>synchronised static 静态方法锁是锁的整个类,所有的对象(上升到类级别)过来都会阻塞</li>
</ul>
<ol start="24">
<li>IO:文件的读写创建</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">public static void La() throws IOException &#123;</span><br><span class="line">        File file = new File(&quot;C:\\Users\\song\\Desktop\\aaa\\a.txt&quot;);</span><br><span class="line">        if (file.exists()) &#123;</span><br><span class="line">            System.out.println(&quot;文件已经存在&quot;);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            //新建并写入</span><br><span class="line">            file.createNewFile();</span><br><span class="line">            PrintWriter printWriter = new PrintWriter(file);</span><br><span class="line">            printWriter.print(&quot;现在输入了第一句话&quot;);</span><br><span class="line">            printWriter.println(399);</span><br><span class="line">            printWriter.print(&quot;这是最后一句话&quot;);</span><br><span class="line">            printWriter.close();</span><br><span class="line">        &#125;</span><br><span class="line">        if (file.exists()) &#123;</span><br><span class="line">            //读取</span><br><span class="line">            Scanner scanner = new Scanner(file);</span><br><span class="line">            while (scanner.hasNext()) &#123;</span><br><span class="line">                String print = scanner.nextLine();</span><br><span class="line">                System.out.println(print);</span><br><span class="line">            &#125;</span><br><span class="line">            scanner.close();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<ol start="25">
<li></li>
</ol>
<ul>
<li>字符流：中文英文;    字节流:二进制 </li>
<li>装饰模式 和 适配器模式</li>
</ul>
<p><img src="https://s3.didiyunapi.com/sxk/pic/io/io.jpg" alt="image"></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">public PrintWriter(File file) throws FileNotFoundException &#123;</span><br><span class="line">    this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))),</span><br><span class="line">         false);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>字节流 读写:  (字节流既能操作字节又能操作字符)</li>
<li>直接复制读写不会出什么错</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args) throws Exception &#123;</span><br><span class="line">     InputStream in = new FileInputStream(new File(&quot;C:\\Users\\song\\Desktop\\aaa\\a.txt&quot;));</span><br><span class="line">     OutputStream out = new FileOutputStream(new File(&quot;C:\\Users\\song\\Desktop\\aaa\\b.txt&quot;));</span><br><span class="line">     int ch;</span><br><span class="line">     while ((ch = in.read()) != -1) &#123;</span><br><span class="line">         out.write(ch);</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>但是要输输出就不得行了,如果是中文的话一个字符是两个即以上的字节,所以要用数组来装并且还需要源文件是UTF-8的才能处理;</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args) throws Exception &#123;</span><br><span class="line">       InputStream in = new FileInputStream(new File(&quot;C:\\Users\\song\\Desktop\\aaa\\a.txt&quot;));</span><br><span class="line">       OutputStream out = new FileOutputStream(new File(&quot;C:\\Users\\song\\Desktop\\aaa\\b.txt&quot;));</span><br><span class="line">       int ch;</span><br><span class="line">       String str = &quot;&quot;;</span><br><span class="line">       byte[] buff = new byte[512];</span><br><span class="line">       while ((ch = in.read(buff)) != -1) &#123;</span><br><span class="line">           //ch表示是读到第几个了;read(buff)表示将读到的东西装进buff数组里面去</span><br><span class="line">           str = str + new String(buff, 0, ch);</span><br><span class="line">           //将数组中的字符串拼接</span><br><span class="line">           System.out.print(str);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<ul>
<li><p>上面是自定义了一个缓存区但是容量大小不稳定;所以就有了一个带缓冲区的读：即Buffer…</p>
</li>
<li><p>字符流：  就可以将中文字符完全正确的输出</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args) throws Exception &#123;</span><br><span class="line">     Reader reader = new FileReader(&quot;C:\\Users\\song\\Desktop\\aaa\\a.txt&quot;);</span><br><span class="line">     int n;</span><br><span class="line">     while ((n = reader.read()) != -1) &#123;</span><br><span class="line">         System.out.print((char) n);</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<ul>
<li><p>读取出来并写入</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args) throws Exception &#123;</span><br><span class="line">    Reader reader = new FileReader(&quot;C:\\Users\\song\\Desktop\\aaa\\a.txt&quot;);</span><br><span class="line">    Writer writer = new FileWriter(&quot;C:\\Users\\song\\Desktop\\aaa\\b.txt&quot;);</span><br><span class="line">    int n;</span><br><span class="line">    while ((n = reader.read()) != -1) &#123;</span><br><span class="line">        writer.write((char) n);</span><br><span class="line">        System.out.print((char) n);</span><br><span class="line">    &#125;</span><br><span class="line">    writer.close();</span><br><span class="line">    //要注意一定要关流</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>转换流, 将字节流转换成字符流</p>
</li>
<li><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Reader inputStreamReader = new InputStreamReader(new FileInputStream(&quot;C:\\Users\\song\\Desktop\\aaa\\a.txt&quot;), &quot;utf-8&quot;);</span><br><span class="line">       inputStreamReader.read();</span><br></pre></td></tr></table></figure>
</li>
<li><p>带缓冲区的读可以读取一整行！</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args) throws Exception &#123;</span><br><span class="line">        Reader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(&quot;C:\\Users\\song\\Desktop\\aaa\\a.txt&quot;), &quot;utf-8&quot;));</span><br><span class="line">        String str = &quot;&quot;;</span><br><span class="line">        String line;</span><br><span class="line">        while ((line = ((BufferedReader) bufferedReader).readLine()) != null) &#123;</span><br><span class="line">        //带缓冲的读可以读取一整一行！</span><br><span class="line">            str = str + line+&quot;\n&quot;;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.print(str);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>带缓冲区的写入,也与上面一致;只是不会包括换行,所以要写入换行的话要用方法：<code>writer.newLine();</code></li>
<li>DataInputStream (可以获取从客户端过来的东西,可以双精度)</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args) throws Exception &#123;</span><br><span class="line">       try (</span><br><span class="line">               DataInputStream inputStream = new DataInputStream(new FileInputStream(&quot;C:\\Users\\song\\Desktop\\aaa\\a.txt&quot;));</span><br><span class="line">               DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(&quot;C:\\Users\\song\\Desktop\\aaa\\b.txt&quot;));</span><br><span class="line">       ) &#123;</span><br><span class="line">           outputStream.writeUTF(&quot;LaLaLa&quot;);</span><br><span class="line">           outputStream.writeDouble(123.456);</span><br><span class="line">           System.out.println(inputStream.readUTF());</span><br><span class="line">           System.out.println(inputStream.readDouble());</span><br><span class="line">           inputStream.close();</span><br><span class="line">           outputStream.close();</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/05/01/First-Bolg/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/05/01/First-Bolg/" itemprop="url">First Bolg</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-01T02:17:59+08:00">
                2018-05-01
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            
          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/04/29/Nginx/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/04/29/Nginx/" itemprop="url">Nginx</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-04-29T02:17:59+08:00">
                2018-04-29
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <blockquote>
<p>Author:<a href="mailto:songxiankun2008@126.com" target="_blank" rel="noopener">songxiankun2008@126.com</a></p>
</blockquote>
<h3 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h3><h5 id="1-负载均衡高可用"><a href="#1-负载均衡高可用" class="headerlink" title="1. 负载均衡高可用"></a>1. 负载均衡高可用</h5><hr>
<h6 id="1-单机Tomcat"><a href="#1-单机Tomcat" class="headerlink" title="1. 单机Tomcat"></a>1. 单机Tomcat</h6><ul>
<li>优点：开发简单 部署简单</li>
<li>缺点：单点故障 出错不可访问</li>
<li>容量：一般200-300;经过优化后可能达到 500/s</li>
<li>巨容易出错,且没救</li>
</ul>
<h6 id="2-多个Tomcat"><a href="#2-多个Tomcat" class="headerlink" title="2. 多个Tomcat:"></a>2. 多个Tomcat:</h6><ul>
<li>优点：轮训算法 能够排除单点故障 并且容量提升</li>
<li>缺点：应用服务器 全部暴露在外网 入口不集中 不安全 并且ip地址是由域名服务商提供的,总会有一半的用户会请求到坏的那个ip地址里面的服务器里面去</li>
<li>容易出错,部分用户有救</li>
</ul>
<h6 id="3-请求分发-负载均衡-多个Tomcat之上"><a href="#3-请求分发-负载均衡-多个Tomcat之上" class="headerlink" title="3. 请求分发/负载均衡 (多个Tomcat之上)"></a>3. 请求分发/负载均衡 (多个Tomcat之上)</h6><ul>
<li>优点：<ul>
<li>一个ip地址进来后再进行分配到不同的服务器(对外网只暴露了接口,没有暴露应用服务器,由内网来分配)</li>
<li>负载均衡典型算法 轮询 随机 </li>
<li>同样的大容量</li>
</ul>
</li>
<li>缺点：<ul>
<li>“请求分发/负载均衡”这个设备或者服务器出问题,那么就是单点故障;不会分配到任何一个服务器</li>
<li>还不如上面第2点还有救</li>
</ul>
</li>
<li>不容易出错,但是出错就没救</li>
</ul>
<h6 id="4-IP回传、多个请求分发的服务器-双机热备-要经过“存活检测”-基于多Tomcat"><a href="#4-IP回传、多个请求分发的服务器-双机热备-要经过“存活检测”-基于多Tomcat" class="headerlink" title="4. IP回传、多个请求分发的服务器(==双机热备==)要经过“存活检测”(基于多Tomcat)"></a>4. IP回传、多个请求分发的服务器(==双机热备==)要经过“存活检测”(基于多Tomcat)</h6><pre><code>双机热备：特指基于高可用系统中的两台服务器的热备。双机高可用按工作中的切换方式分为：主-备方式（Active-Standby方式）和双主机方式（Active-Active方式）;
主-备方式即指的是一台服务器处于某种业务的激活状态（即Active状态），另一台服务器处于该业务的备用状态（即Standby状态)。
而双主机方式即指两种不同业务分别在两台服务器上互为主备状态（即Active-Standby和Standby-Active状态）。
</code></pre><ul>
<li>优点：同一时刻 只有一个在服务,可能每一个几秒(自定义时间间隔)都会存活检测去检测服务器是否正常运行,如果服务器挂点,存活检测就会修改ip地址让用户请求到另外一个正常的服务器上</li>
<li>缺点：<ul>
<li>利用率低;只利用了50%,另外一个正常服务器处于空闲状态没有被利用即50%空闲且不能关闭这个空闲的服务器</li>
<li>请求分发设备 也有流量并发上限 eg:100W 瓶颈</li>
</ul>
</li>
</ul>
<h6 id="5-双机热备-DNS轮训-基于多Tomcat"><a href="#5-双机热备-DNS轮训-基于多Tomcat" class="headerlink" title="5. 双机热备 + DNS轮训 (基于多Tomcat)"></a>5. 双机热备 + DNS轮训 (基于多Tomcat)</h6><p>在域名服务商有多个IP地址,用户通过不同的IP地址进来后会分配到不同的“请求分发/负载均衡  心跳检测”服务区中(即分配到不同的“双机热备”上),每一套不同的” 请求分发/负载均衡 存活检测”又进行轮训或者随机算法到不同的Tomcat服务器上</p>
<p>保守估计 一天几万，一个月几百万的访问量;具体看服务看实际架构</p>
<h5 id="2-常见负载均衡设备优缺点-Tomcat压力测试"><a href="#2-常见负载均衡设备优缺点-Tomcat压力测试" class="headerlink" title="2. 常见负载均衡设备优缺点 - Tomcat压力测试"></a>2. 常见负载均衡设备优缺点 - Tomcat压力测试</h5><hr>
<p>负载均衡：</p>
<ul>
<li>硬件实现： <ul>
<li>常用设备 F5 会分发到这个F5的ip上面</li>
<li><ol>
<li>优点：非常快 可靠性高 并发量大</li>
</ol>
</li>
<li><ol start="2">
<li>缺点：太贵 成本高 不方便</li>
</ol>
</li>
</ul>
</li>
<li>软件实现：<ul>
<li>常用①：Linux中的LVS 基于Linux系统IP层面的负载均衡    <ul>
<li><ol>
<li>优点：可靠性非常高 简单易用 并发量大</li>
</ol>
</li>
<li><ol start="2">
<li>缺点：将动态和静态文件都负载在Tomcat上不能实现动静分离</li>
</ol>
</li>
</ul>
</li>
<li>常用②：Nginx<ul>
<li><ol>
<li>优点：是个软件,不是硬件,也不是像LVS这样基于Linux的小系统;Nginx是个软件,==可以将静态资源和动态资源放在Nginx里面不同的目录里面;如果访问中有静态资源,那么静态资源不会经常Tomcat就直接返回到浏览器,而动态资源等需要请求数据库的才通过Tomcat服务器进入到数据库里面再把值带出来==;这样相当于前几者都大大降低了服务器的压力且提高了静态资源的访问速度</li>
</ol>
</li>
</ul>
</li>
<li>常用③: Apache Http-Server<ul>
<li><ol>
<li>优点：跟Nginx差不多</li>
</ol>
</li>
<li><ol start="2">
<li>缺点：效率比Nginx低很多</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>正向代理：   用户将自己的ip隐藏;

反向代理：   服务器将自己的ip给隐藏了
</code></pre><p>常用压力测试软件： jmeter </p>
<h5 id="3-Nginx负载均衡实战-常见负载均衡算法解析"><a href="#3-Nginx负载均衡实战-常见负载均衡算法解析" class="headerlink" title="3. Nginx负载均衡实战-常见负载均衡算法解析"></a>3. Nginx负载均衡实战-常见负载均衡算法解析</h5><hr>
<p>配置： nginx-1.12.2\conf\nginx.conf</p>
<pre><code>#upstream关键字表示上游服务器真正的应用服务器配置
#test-service 表示这个配置的名称
upstream test-service { #服务器集群名字,此处叫做test-service
        server  localhost:8090; #服务器配置
        server    localhost:8080;
}
//上面这三行是需要添加的

server {
    #表示监听本机端口80
    listen       80;
    #服务器名称
    server_name  localhost;

    #charset koi8-r;

    #access_log  logs/host.access.log  main;
    #此处的&quot;/&quot; 表示localhost下的所有请求
    location / {
        root   html;
        index  index.html index.htm;

        #请求转发,此处的路径就是上面配置的名称
        proxy_pass http://test-service;
  //上面这一行是需要添加的

    }

    #error_page  404              /404.html;
</code></pre><p>启动：</p>
<ul>
<li>nginx.exe -&gt; localhost:80</li>
<li>启动localhost:8080 和 localhost:8090 两个Tocmat</li>
<li style="list-style: none"><input type="checkbox" checked> 此时打开localhost:80;就会经过默认的“轮询”算法轮到这两个Tocmat启动的项目中去,可能上一秒是8080的项目下秒就是8090的项目</li>
</ul>
<h6 id="算法解析："><a href="#算法解析：" class="headerlink" title="算法解析："></a>算法解析：</h6><pre><code>public class IpMap {
    /**
     * 待路由的Ip列表,Key代表Ip,Value代表该Ip的权重
     */
    public static HashMap&lt;String,Integer&gt; serverWeigetMap = new HashMap&lt;String, Integer&gt;();
    static {
        serverWeigetMap.put(&quot;192.168.1.100&quot;, 2);
        serverWeigetMap.put(&quot;192.168.1.101&quot;, 3);
        serverWeigetMap.put(&quot;192.168.1.102&quot;, 1);
        serverWeigetMap.put(&quot;192.168.1.103&quot;, 2);
        serverWeigetMap.put(&quot;192.168.1.104&quot;, 1);
    }
}
</code></pre><p>==轮询算法 RoundRobin==</p>
<pre><code>public class RoundRobin {
    /**
     * 轮询算法
     */
    private static Integer pos = 0;
    public static String getServer() {
        //重建一个Map,避免服务器的上下限导致的并发问题
        Map&lt;String, Integer&gt; serverMap = new HashMap&lt;String, Integer&gt;();
        serverMap.putAll(IpMap.serverWeigetMap);
        //取得Ip地址List
        Set keySet = serverMap.keySet();
        ArrayList&lt;String&gt; keyList = new ArrayList&lt;String&gt;();
        keyList.addAll(keySet);
        String server = null;
        synchronized (pos) {
            if (pos &gt; keySet.size()) {
                pos = 0;
            }
            server = keyList.get(pos);
            pos++;
        }
        return null;
    }
}
</code></pre><p>==随机算法 RandomArithmetic==</p>
<pre><code>public class RandomArithmetic {
    public static String getServer() {
        //重建一个Map,避免服务器的上下限导致的并发问题
        Map&lt;String, Integer&gt; serverMap = new HashMap&lt;String, Integer&gt;();
        serverMap.putAll(IpMap.serverWeigetMap);
        //取得Ip地址List
        Set keySet = serverMap.keySet();
        ArrayList&lt;String&gt; keyList = new ArrayList&lt;String&gt;();
        keyList.addAll(keySet);
        Random random = new Random();
        int randomPos = random.nextInt(keyList.size());
        return keyList.get(randomPos);
    }
}
</code></pre><p>==源地址哈希 Hash==</p>
<ul>
<li>最后的那几行代码很重要;</li>
<li>不同于上面的“随机”和“轮询”;</li>
<li>这种算法  每次都会着陆于同一台Tomcat上</li>
</ul>
<pre><code>public class Hash {
    public static String getServer() {
        //重建一个Map,避免服务器的上下限导致的并发问题
        Map&lt;String, Integer&gt; serverMap = new HashMap&lt;String, Integer&gt;();
        serverMap.putAll(IpMap.serverWeigetMap);
        //取得Ip地址List
        Set keySet = serverMap.keySet();
        ArrayList&lt;String&gt; keyList = new ArrayList&lt;String&gt;();
        keyList.addAll(keySet);
        // 在web应用中可通过HttpServer的getRemoteIp方法获取
        String remoteIp = &quot;127.0.0.1&quot;;
        int HashCode = remoteIp.hashCode();
        int severListSize = keyList.size();
        int serverPos = HashCode % severListSize;
        return keyList.get(serverPos);
    }
}
</code></pre><p>==加权轮询==</p>
<ul>
<li>增加权利,同时增加责任;加大被分配上的几率</li>
<li><p>应用场景：比如一个内存4G,一个内存16G;那么就需要加权到16G的机器上</p>
<pre><code>public class WeightRoundRobin {
    private static Integer pos;
    public static String getServer() {
        //重建一个Map,避免服务器的上下限导致的并发问题
        Map&lt;String, Integer&gt; serverMap = new HashMap&lt;String, Integer&gt;();
        serverMap.putAll(IpMap.serverWeigetMap);
        //取得Ip地址List
        Set&lt;String&gt; keySet = serverMap.keySet();
        Iterator&lt;String&gt; iterator = keySet.iterator();
        List&lt;String&gt; serverList = new ArrayList&lt;String&gt;();
        while (iterator.hasNext()) {
            String server = iterator.next();
            int weight = serverMap.get(server);
            for (int i = 0; i &lt; weight; i++) {
                //此处 重复添加;被轮询到的概率增大
                serverList.add(server);
            }
        }
        String server = null;
        synchronized (pos) {
            if (pos &gt; keySet.size()) {
                pos = 0;
                server = serverList.get(pos);
                pos++;
            }
        }
        return server;
    }
}
</code></pre></li>
</ul>
<h6 id="权重轮询的配置："><a href="#权重轮询的配置：" class="headerlink" title="权重轮询的配置："></a>权重轮询的配置：</h6><pre><code>#upstream关键字表示上游服务器真正的应用服务器配置
#test-service 表示这个配置的名称
upstream test-service { #服务器集群名字,此处叫做test-service
        server  localhost:8090; weight 10 #表示轮询为10
        server    localhost:8080;
}
</code></pre><ul>
<li>加权随机(Weight Random)法</li>
<li>最小连接数(Least Connections)法——据后端服务器当前的连接情况,动态的选取其中当前积压连接数最少的一台服务器来处理当前请求</li>
</ul>
<h5 id="4-负载均衡带来的问题以及解决方法"><a href="#4-负载均衡带来的问题以及解决方法" class="headerlink" title="4. 负载均衡带来的问题以及解决方法"></a>4. 负载均衡带来的问题以及解决方法</h5><hr>
<h6 id="1"><a href="#1" class="headerlink" title="1."></a>1.</h6><ul>
<li>问题： 轮询到不同的Tomcat上,两个Tomcat上的session不同步,即操作的数据也可能不同步;</li>
<li>解决： 应用服务器session同步</li>
<li><p>具体操作：在两个(或多个Tomcat中 添加 以下配置(集群配置))</p>
<pre><code>&lt;Cluster className=&quot;org.apache.catalina.ha.tcp.SimpleTcpCluster&quot;  
                channelSendOptions=&quot;8&quot;&gt;  

         &lt;Manager className=&quot;org.apache.catalina.ha.session.DeltaManager&quot;  
                  expireSessionsOnShutdown=&quot;false&quot;  
                  notifyListenersOnReplication=&quot;true&quot;/&gt;  

         &lt;Channel className=&quot;org.apache.catalina.tribes.group.GroupChannel&quot;&gt;  
           &lt;Membership className=&quot;org.apache.catalina.tribes.membership.McastService&quot;  
                       address=&quot;228.0.0.4&quot;  
                       port=&quot;45564&quot;  
                       frequency=&quot;500&quot;  
                       dropTime=&quot;3000&quot;/&gt;  
           &lt;Receiver className=&quot;org.apache.catalina.tribes.transport.nio.NioReceiver&quot;  
                     address=&quot;auto&quot;  
                     port=&quot;4000&quot;  
                     autoBind=&quot;100&quot;  
                     selectorTimeout=&quot;5000&quot;  
                     maxThreads=&quot;6&quot;/&gt;  

           &lt;Sender className=&quot;org.apache.catalina.tribes.transport.ReplicationTransmitter&quot;&gt;  
           &lt;Transport className=&quot;org.apache.catalina.tribes.transport.nio.PooledParallelSender&quot;/&gt;  
           &lt;/Sender&gt;  
           &lt;Interceptor className=&quot;org.apache.catalina.tribes.group.interceptors.TcpFailureDetector&quot;/&gt;  
           &lt;Interceptor className=&quot;org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor&quot;/&gt;  
         &lt;/Channel&gt;  

         &lt;Valve className=&quot;org.apache.catalina.ha.tcp.ReplicationValve&quot;  
                filter=&quot;&quot;/&gt;  
         &lt;Valve className=&quot;org.apache.catalina.ha.session.JvmRouteBinderValve&quot;/&gt;  

         &lt;Deployer className=&quot;org.apache.catalina.ha.deploy.FarmWarDeployer&quot;  
                   tempDir=&quot;/tmp/war-temp/&quot;  
                   deployDir=&quot;/tmp/war-deploy/&quot;  
                   watchDir=&quot;/tmp/war-listen/&quot;  
                   watchEnabled=&quot;false&quot;/&gt;  

         &lt;ClusterListener className=&quot;org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener&quot;/&gt;  
         &lt;ClusterListener className=&quot;org.apache.catalina.ha.session.ClusterSessionListener&quot;/&gt;  
       &lt;/Cluster&gt;  
</code></pre><p>然后再修改系统PATH,把这两个放在同一个文件夹中…..各种巨麻烦</p>
<p>   来源：  <a href="http://blog.csdn.net/wlwlwlwl015/article/details/48160433" target="_blank" rel="noopener">http://blog.csdn.net/wlwlwlwl015/article/details/48160433</a></p>
</li>
<li>缺点：应用服务器本身能力有限 再加上session同步 带来的网络开销越来越大</li>
<li>所以基本都不用这种方法<h6 id="2"><a href="#2" class="headerlink" title="2."></a>2.</h6></li>
<li>问题：与1. 同样的问题</li>
<li>解决：上面的算法中有个“源地址哈希”算法;</li>
<li>操作：每次都会请求分发到同一台的服务器上,所以session根本不会变;(是将用户进行随机或者轮询到不同的服务器上,那么这个用户下次来的时候就会一直在这台服务器上不会再改变所以,session不会产生新的也不会变)</li>
<li>缺点：这样的方式,集群容灾的特性就体现不出来了</li>
<li><p>配置：</p>
<pre><code>#upstream关键字表示上游服务器真正的应用服务器配置
    #test-service 表示这个配置的名称
    upstream test-service { #服务器集群名字,此处叫做test-service
            server  localhost:8090; #服务器配置
            server    localhost:8080;
            ip_hash; #仅添加了这个,表示使用&quot;源地址哈希&quot;算法才分配用户
    }
</code></pre></li>
<li><p>备注：为了解决并发问题不解决容灾或者故障的话可以考虑用这个</p>
</li>
</ul>
<h6 id="3"><a href="#3" class="headerlink" title="3."></a>3.</h6><ul>
<li>问题：与上同样问题</li>
<li>解决： ==把session外置==(到redis);Nginx可以用随机轮询等</li>
<li>能够彻底解决问题,类似于一种单点登录</li>
</ul>
<h5 id="5-Nginx动静分离-双机热备原理"><a href="#5-Nginx动静分离-双机热备原理" class="headerlink" title="5. Nginx动静分离-双机热备原理"></a>5. Nginx动静分离-双机热备原理</h5><hr>
<h6 id="动静分离配置："><a href="#动静分离配置：" class="headerlink" title="动静分离配置："></a>动静分离配置：</h6><pre><code>#upstream关键字表示上游服务器真正的应用服务器配置
#test-service 表示这个配置的名称
upstream test-service { #服务器集群名字,此处叫做test-service
        server  localhost:8090; #服务器配置
        server    localhost:8080;
        ip_hash; #仅添加了这个,表示使用&quot;源地址哈希&quot;算法才分配用户
}

server {
    #表示监听本机端口80
    listen       80;
    #服务器名称
    server_name  localhost;

    #charset koi8-r;

    #所有静态请求都由nginx处理,存放目录为html
    location ~ \.(gif|jpg|jpeg|png|bmp|swf)$ { 
    #请求匹配的正则表达式,所有以 上面 这些后缀结尾的文件
        root     html;
    # 全部到Nginx本地根目录下的 html 文件夹里面去找
    # expires 5d; #图片在本地缓存5天,就不用往后台请求了
    }

    #access_log  logs/host.access.log  main;
    #此处的&quot;/&quot; 表示localhost下的所有请求
    location / {
        root   html;
        index  index.html index.htm;
        #除了上面的静态资源之外的动态请求数据的资源
        #全部都请求转发到后台服务器上去
        proxy_pass http://test-service;

    }
</code></pre><h6 id="双机热备："><a href="#双机热备：" class="headerlink" title="双机热备："></a>双机热备：</h6><ul>
<li>双机热备中用到的软件是<code>Keepalived</code>;</li>
<li>仅支持<code>Linux</code>系统并且要在两台电脑或者虚拟机下才能完成</li>
<li>有一个Master的Nginx01,有个Backup的Nginx02;有一个KeepAlived的VirtualIP;</li>
<li>当Master的Nginx01挂了后,KeepAlived会立即通过虚拟IP转到备用的Nginx02上;</li>
</ul>
<h5 id="6-Ngnix…总结"><a href="#6-Ngnix…总结" class="headerlink" title="6. Ngnix…总结"></a>6. Ngnix…总结</h5><hr>
<ul>
<li style="list-style: none"><input type="checkbox" checked> Nginx后面主要跟的是应用型服务器比如Tomcat(Http请求这类的),当然也可以跟Mysql,只是Mysql有它单独的一套;</li>
<li style="list-style: none"><input type="checkbox" checked> Nginx算法：</li>
<li><ol>
<li>轮询(默认)<ul>
<li>每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除</li>
</ul>
</li>
</ol>
</li>
<li><ol start="2">
<li>Weight<ul>
<li>指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况</li>
</ul>
</li>
</ol>
</li>
<li><ol start="3">
<li>IP Hash<ul>
<li>每个请求按访问ip的Hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题</li>
</ul>
</li>
</ol>
</li>
<li><ol start="4">
<li>Fair (第三方)<ul>
<li>按后端服务器的响应时间来分配请求,响应时间的优先分配</li>
</ul>
</li>
</ol>
</li>
<li><ol start="5">
<li>url_hash (第三方)<ul>
<li>按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效</li>
</ul>
</li>
</ol>
</li>
</ul>
<hr>
<h6 id="OSI层级协议"><a href="#OSI层级协议" class="headerlink" title="OSI层级协议"></a>OSI层级协议</h6><ul>
<li style="list-style: none"><input type="checkbox"> 所谓四层就是基于 IP + 端口 的负载均衡</li>
<li style="list-style: none"><input type="checkbox"> 七层就是基于URL等应用层信息的负载均衡</li>
<li style="list-style: none"><input type="checkbox"> 同理,还有基于MAC地址的二层负载均衡和基于IP地址的三层负载均衡</li>
<li style="list-style: none"><input type="checkbox" checked> 换句话说,<strong>二层负载均衡</strong>会通过一个虚拟MAC地址接收请求,单后再分配到真实的MAC地址;<strong>三层负载均衡</strong>会通过一个虚拟IP地址接收请求,然后再分配到真实的IP地址;<strong>四层</strong>通过虚拟 IP+端口 接收请求,然后再分配到真实的服务器;<strong>七层</strong>通过虚拟的URL或主机名接收请求,然后再分配到真实的服务器;</li>
</ul>
<h6 id="优缺点："><a href="#优缺点：" class="headerlink" title="优缺点："></a>优缺点：</h6><ul>
<li style="list-style: none"><input type="checkbox" checked> 七层负载均衡也称为“内容交换”,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。</li>
<li style="list-style: none"><input type="checkbox" checked> 以常见的TCP为例,负载均衡设备如果要根据真正的应用层内容再选择服务器,只能先代理最终的服务器和客户端建立连接(三次握手)后,才可能接受到客户端发送的真正应用层内容的报文,然后再根据该报文中的特定字段,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。负载均衡设备在这种情况下,更类似于一个代理服务器。负载均衡和前端的客户端以及后端的服务器会分别建立TCP连接。所以从这个技术原理上来看,七层负载均衡明显的对负载均衡设备要求更高,处理七层的能力也必然会低于四层模式的部署方式。</li>
<li>更智能</li>
</ul>
<p><img src="https://s3.didiyunapi.com/sxk/pic/nginx/1.jpg" alt="image"></p>
<p><img src="https://s3.didiyunapi.com/sxk/pic/nginx/2.jpg" alt="image"></p>
<p><img src="https://s3.didiyunapi.com/sxk/pic/nginx/3.jpg" alt="image"></p>
<p><img src="https://s3.didiyunapi.com/sxk/pic/nginx/tcp-ip.gif" alt="image"></p>
<p><img src="https://s3.didiyunapi.com/sxk/pic/nginx/nginx.jpg" alt="image"></p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/04/19/init/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/04/19/init/" itemprop="url">Init</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-04-19T03:17:59+08:00">
                2018-04-19
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h6 id="IDEA-Templet"><a href="#IDEA-Templet" class="headerlink" title="IDEA Templet"></a>IDEA Templet</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line">  * Created By  醉美柳舞之众星捧月</span><br><span class="line">  * @author $&#123;USER&#125; </span><br><span class="line">  * @date $&#123;DATE&#125; $&#123;TIME&#125;</span><br><span class="line">  */</span><br><span class="line">  </span><br><span class="line">/**</span><br><span class="line">    * Author song  醉美柳舞之众星捧月</span><br><span class="line">    * Date &amp; Time  $DATA$ $TIME$</span><br><span class="line">    * Description  $END$</span><br><span class="line">    */</span><br></pre></td></tr></table></figure>
<h6 id="CentOS："><a href="#CentOS：" class="headerlink" title="CentOS："></a>CentOS：</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">systemctl set-default graphical.target 由命令行模式更改为图形界面模式</span><br><span class="line"></span><br><span class="line">systemctl set-default multi-user.target 由图形界面模式更改为命令行模式</span><br></pre></td></tr></table></figure>
<h6 id="释放内存："><a href="#释放内存：" class="headerlink" title="释放内存："></a>释放内存：</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">1. 释放网页缓存(To free pagecache):</span><br><span class="line"></span><br><span class="line">sync; echo 1 &gt; /proc/sys/vm/drop_caches</span><br><span class="line"></span><br><span class="line">2. 释放目录项和索引(To free dentries and inodes):</span><br><span class="line"></span><br><span class="line">sync; echo 2 &gt; /proc/sys/vm/drop_caches</span><br><span class="line"></span><br><span class="line">3. 释放网页缓存，目录项和索引（To free pagecache, dentries and inodes）:</span><br><span class="line"></span><br><span class="line">sync; echo 3 &gt; /proc/sys/vm/drop_caches</span><br></pre></td></tr></table></figure>
<h6 id="port"><a href="#port" class="headerlink" title="port:"></a>port:</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">netstat -nupl (UDP类型的端口)</span><br><span class="line">netstat -ntpl (TCP类型的端口)</span><br><span class="line">netstat -anp 显示系统端口使用情况</span><br><span class="line"></span><br><span class="line">-   a 表示所有</span><br><span class="line">-   n 表示不查询dns (显示ip,可以加速操作)</span><br><span class="line">-   t 表示tcp协议</span><br><span class="line">-   u 表示udp协议</span><br><span class="line">-   p 表示查询占用的程序</span><br><span class="line">-   l 表示查询正在监听的程序</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"></span><br><span class="line">netstat -ntlp   //查看当前所有tcp端口·</span><br><span class="line">netstat -ntulp |grep 80   //查看所有80端口使用情况·</span><br><span class="line">netstat -an | grep 3306   //查看所有3306端口使用情况·</span><br><span class="line">netstat  -lanp  //查看一台服务器上面哪些服务及端口</span><br><span class="line">ps -ef |grep mysqld //查看一个服务有几个端口。比如要查看mysqld</span><br><span class="line">netstat -pnt |grep :3306 |wc    //查看某一端口的连接数量,比如3306端口</span><br><span class="line">netstat -anp |grep 3306 //查看某一端口的连接客户端IP 比如3306端口</span><br><span class="line">netstat -an //查看网络端口 </span><br><span class="line"></span><br><span class="line">lsof -i :port，使用lsof -i :port就能看见所指定端口运行的程序，同时还有当前连接。</span><br></pre></td></tr></table></figure>
<h6 id="rpm"><a href="#rpm" class="headerlink" title="rpm:"></a>rpm:</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">rpm 常用命令</span><br><span class="line">1.安装一个包 </span><br><span class="line"># rpm -ivh </span><br><span class="line"></span><br><span class="line">2.升级一个包 </span><br><span class="line"># rpm -Uvh </span><br><span class="line"></span><br><span class="line">3.卸载一个包 </span><br><span class="line"># rpm -e </span><br><span class="line">如果遇到依赖，无法删除，使用 rpm -e --nodeps &lt;包的名字&gt; 不检查依赖，直接删除rpm包。 </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">4.查询一个包是否被安装 </span><br><span class="line"># rpm -q &lt; rpm package name&gt; </span><br><span class="line"></span><br><span class="line">5.得到被安装的包的信息 </span><br><span class="line"># rpm -qi &lt; rpm package name&gt; </span><br><span class="line"></span><br><span class="line">tar -cvjpf etc.tar.bz2 /etc　　//-c为创建一个打包文件，相应的-f后面接创建的文件的名称，使用了.tar.bz2后缀，-j标志使用bzip2压缩，最后面为具体的操作对象/etc目录</span><br></pre></td></tr></table></figure>
<h6 id="redis"><a href="#redis" class="headerlink" title="redis"></a>redis</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">1. 安装redis编译的c环境，yum install gcc-c++</span><br><span class="line"></span><br><span class="line">2. 将redis-2.6.16.tar.gz上传到Linux系统中</span><br><span class="line"></span><br><span class="line">3. 解压到/usr/local下  tar -xvf redis-2.6.16.tar.gz -C /usr/local</span><br><span class="line"></span><br><span class="line">4. 进入redis-2.6.16目录 使用make命令编译redis</span><br><span class="line"></span><br><span class="line">5. 在redis-2.6.16目录中 使用make PREFIX=/usr/local/redis install命令安装			redis到/usr/local/redis中</span><br><span class="line"></span><br><span class="line">6. 拷贝redis-2.6.16中的redis.conf到安装目录redis中</span><br><span class="line"></span><br><span class="line">7. 启动redis 在bin下执行命令redis-server redis.conf</span><br><span class="line"></span><br><span class="line">8. 如需远程连接redis，需配置redis端口6379在linux防火墙中开发</span><br><span class="line">/sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT</span><br><span class="line">/etc/rc.d/init.d/iptables save</span><br><span class="line"></span><br><span class="line">9. 将redis.conf文件中的daemonize从false修改成true表示后台启动</span><br><span class="line"></span><br><span class="line">10. 若要远程连接的话,1.&lt;code&gt;bind 127.0.0.1改为 #bind 127.0.0.1&lt;/code&gt;,2.&lt;code&gt;protected-mode yes 改为 protected-mode no&lt;/code&gt;</span><br></pre></td></tr></table></figure>
<h6 id="MySQL"><a href="#MySQL" class="headerlink" title="MySQL"></a>MySQL</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">CentOS7默认数据库是mariadb,配置等用着不习惯,因此决定改成mysql,但是CentOS7的yum源中默认好像是没有mysql的。为了解决这个问题，我们要先下载mysql的repo源。</span><br><span class="line"></span><br><span class="line">1.下载mysql的repo源</span><br><span class="line"></span><br><span class="line">$ wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm</span><br><span class="line"></span><br><span class="line">2.安装mysql-community-release-el7-5.noarch.rpm包</span><br><span class="line"></span><br><span class="line">$ sudo rpm -ivh mysql-community-release-el7-5.noarch.rpm</span><br><span class="line"></span><br><span class="line">安装这个包后，会获得两个mysql的yum repo源：/etc/yum.repos.d/mysql-community.repo，/etc/yum.repos.d/mysql-community-source.repo。</span><br><span class="line"></span><br><span class="line">3.安装mysql</span><br><span class="line"></span><br><span class="line">$ sudo yum install mysql-server</span><br><span class="line"></span><br><span class="line">根据提示安装就可以了,不过安装完成后没有密码,需要重置密码</span><br><span class="line"></span><br><span class="line">4.重置mysql密码</span><br><span class="line"></span><br><span class="line">$ mysql -u root</span><br><span class="line"></span><br><span class="line">登录时有可能报这样的错：ERROR 2002 (HY000): Can‘t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock‘ </span><br><span class="line">(2)，原因是/var/lib/mysql的访问权限问题。下面的命令把/var/lib/mysql的拥有者改为当前用户：</span><br><span class="line"></span><br><span class="line">$ sudo chown -R root:root /var/lib/mysql</span><br><span class="line"></span><br><span class="line">重启mysql服务</span><br><span class="line"></span><br><span class="line">$ service mysqld restart</span><br><span class="line"></span><br><span class="line">接下来登录重置密码：</span><br><span class="line"></span><br><span class="line">$ mysql -u root  //直接回车进入mysql控制台</span><br><span class="line"></span><br><span class="line">mysql &gt; use mysql;</span><br><span class="line"></span><br><span class="line">mysql &gt; update user set password=password(&apos;war3sxkcs&apos;) where user=&apos;root&apos;;</span><br><span class="line"></span><br><span class="line">mysql &gt; grant all privileges on *.* to &apos;root&apos;@&apos;%&apos; identified by &apos;war3sxkcs&apos; with grant option; //开启远程可连接</span><br><span class="line"></span><br><span class="line">mysql &gt; flush privileges;</span><br><span class="line"></span><br><span class="line">mysql &gt; exit;</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"></span><br><span class="line">查看mysql的安装</span><br><span class="line">rpm -qa|grep -i mysql</span><br><span class="line"></span><br><span class="line">停止/卸载mysql</span><br><span class="line">rpm -e --noscripts MySQL-client-5.5.25a-1.rhel5</span><br><span class="line"></span><br><span class="line">rpm -ev mysql-community-libs-5.7.21-1.el7.x86_64 --nodeps</span><br><span class="line"></span><br><span class="line">find / -name mysql</span><br></pre></td></tr></table></figure>
<h6 id="wifi"><a href="#wifi" class="headerlink" title="wifi"></a>wifi</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nmcli dev wifi list</span><br></pre></td></tr></table></figure>
<h6 id="firewall"><a href="#firewall" class="headerlink" title="firewall"></a>firewall</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line">Centos 7 firewall 命令：</span><br><span class="line"></span><br><span class="line">查看已经开放的端口：</span><br><span class="line"></span><br><span class="line">firewall-cmd --list-ports</span><br><span class="line">开启端口</span><br><span class="line"></span><br><span class="line">firewall-cmd --zone=public --add-port=80/tcp --permanent</span><br><span class="line">命令含义：</span><br><span class="line"></span><br><span class="line">–zone #作用域</span><br><span class="line"></span><br><span class="line">–add-port=80/tcp #添加端口，格式为：端口/通讯协议</span><br><span class="line"></span><br><span class="line">–permanent #永久生效，没有此参数重启后失效</span><br><span class="line"></span><br><span class="line">重启防火墙</span><br><span class="line"></span><br><span class="line">firewall-cmd --reload #重启firewall</span><br><span class="line">systemctl stop firewalld.service #停止firewall</span><br><span class="line">systemctl disable firewalld.service #禁止firewall开机启动</span><br><span class="line">firewall-cmd --state #查看默认防火墙状态（关闭后显示notrunning，开启后显示running）</span><br><span class="line">CentOS 7 以下版本 iptables 命令</span><br><span class="line"></span><br><span class="line">如要开放80，22，8080 端口，输入以下命令即可</span><br><span class="line"></span><br><span class="line">/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT</span><br><span class="line">/sbin/iptables -I INPUT -p tcp --dport 22 -j ACCEPT</span><br><span class="line">/sbin/iptables -I INPUT -p tcp --dport 8080 -j ACCEPT</span><br><span class="line">然后保存：</span><br><span class="line"></span><br><span class="line">/etc/rc.d/init.d/iptables save</span><br><span class="line">查看打开的端口：</span><br><span class="line"></span><br><span class="line">/etc/init.d/iptables status</span><br><span class="line">关闭防火墙 </span><br><span class="line">1） 永久性生效，重启后不会复原</span><br><span class="line"></span><br><span class="line">开启： chkconfig iptables on</span><br><span class="line"></span><br><span class="line">关闭： chkconfig iptables off</span><br><span class="line"></span><br><span class="line">2） 即时生效，重启后复原</span><br><span class="line"></span><br><span class="line">开启： service iptables start</span><br><span class="line"></span><br><span class="line">关闭： service iptables stop</span><br><span class="line"></span><br><span class="line">查看防火墙状态： service iptables status</span><br><span class="line"></span><br><span class="line">下面说下CentOS7和6的默认防火墙的区别</span><br><span class="line"></span><br><span class="line">CentOS 7默认使用的是firewall作为防火墙，使用iptables必须重新设置一下</span><br><span class="line"></span><br><span class="line">1、直接关闭防火墙</span><br><span class="line"></span><br><span class="line">systemctl stop firewalld.service #停止firewall</span><br><span class="line"></span><br><span class="line">systemctl disable firewalld.service #禁止firewall开机启动</span><br><span class="line"></span><br><span class="line">2、设置 iptables service</span><br><span class="line"></span><br><span class="line">yum -y install iptables-services</span><br><span class="line"></span><br><span class="line">如果要修改防火墙配置，如增加防火墙端口3306</span><br><span class="line"></span><br><span class="line">vi /etc/sysconfig/iptables </span><br><span class="line"></span><br><span class="line">增加规则</span><br><span class="line"></span><br><span class="line">-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT</span><br><span class="line"></span><br><span class="line">保存退出后</span><br><span class="line"></span><br><span class="line">systemctl restart iptables.service #重启防火墙使配置生效</span><br><span class="line"></span><br><span class="line">systemctl enable iptables.service #设置防火墙开机启动</span><br><span class="line"></span><br><span class="line">最后重启系统使设置生效即可。</span><br><span class="line"></span><br><span class="line">systemctl start iptables.service #打开防火墙</span><br><span class="line"></span><br><span class="line">systemctl stop iptables.service #关闭防火墙</span><br></pre></td></tr></table></figure>
<h6 id="Zookeeper"><a href="#Zookeeper" class="headerlink" title="Zookeeper"></a>Zookeeper</h6><hr>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br></pre></td><td class="code"><pre><span class="line">1、创建 /usr/local/services/zookeeper 文件夹：</span><br><span class="line">    mkdir -p /usr/local/services/zookeeper</span><br><span class="line"> </span><br><span class="line">2、进入到 /usr/local/services/zookeeper 目录中：</span><br><span class="line">    cd /usr/local/services/zookeeper</span><br><span class="line"> </span><br><span class="line">3、下载 zookeeper-3.4.9.tar.gz：</span><br><span class="line">    wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.9/zookeeper-3.4.9.tar.gz</span><br><span class="line"> </span><br><span class="line">4、解压缩 zookeeper-3.4.9.tar.gz：</span><br><span class="line">    tar -zxvf zookeeper-3.4.9.tar.gz</span><br><span class="line"> </span><br><span class="line">5、进入到 /usr/local/services/zookeeper/zookeeper-3.4.9/conf 目录中：</span><br><span class="line">    cd zookeeper-3.4.9/conf/</span><br><span class="line"> </span><br><span class="line">6、复制 zoo_sample.cfg 文件的并命名为为 zoo.cfg：</span><br><span class="line">    cp zoo_sample.cfg zoo.cfg</span><br><span class="line"> </span><br><span class="line">7、用 vim 打开 zoo.cfg 文件并修改其内容为如下：</span><br><span class="line">    # The number of milliseconds of each tick</span><br><span class="line"> </span><br><span class="line">    # zookeeper 定义的基准时间间隔，单位：毫秒</span><br><span class="line">    tickTime=2000</span><br><span class="line"> </span><br><span class="line">    # The number of ticks that the initial </span><br><span class="line">    # synchronization phase can take</span><br><span class="line">    initLimit=10</span><br><span class="line">    # The number of ticks that can pass between </span><br><span class="line">    # sending a request and getting an acknowledgement</span><br><span class="line">    syncLimit=5</span><br><span class="line">    # the directory where the snapshot is stored.</span><br><span class="line">    # do not use /tmp for storage, /tmp here is just </span><br><span class="line">    # example sakes.</span><br><span class="line">    # dataDir=/tmp/zookeeper</span><br><span class="line"> </span><br><span class="line">    # 数据文件夹</span><br><span class="line">    dataDir=/usr/local/services/zookeeper/zookeeper-3.4.9/data</span><br><span class="line"> </span><br><span class="line">    # 日志文件夹</span><br><span class="line">    dataLogDir=/usr/local/services/zookeeper/zookeeper-3.4.9/logs</span><br><span class="line"> </span><br><span class="line">    # the port at which the clients will connect</span><br><span class="line">    # 客户端访问 zookeeper 的端口号</span><br><span class="line">    clientPort=2181</span><br><span class="line"> </span><br><span class="line">    # the maximum number of client connections.</span><br><span class="line">    # increase this if you need to handle more clients</span><br><span class="line">    #maxClientCnxns=60</span><br><span class="line">    #</span><br><span class="line">    # Be sure to read the maintenance section of the </span><br><span class="line">    # administrator guide before turning on autopurge.</span><br><span class="line">    #</span><br><span class="line">    # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance</span><br><span class="line">    #</span><br><span class="line">    # The number of snapshots to retain in dataDir</span><br><span class="line">    #autopurge.snapRetainCount=3</span><br><span class="line">    # Purge task interval in hours</span><br><span class="line">    # Set to &quot;0&quot; to disable auto purge feature</span><br><span class="line">    #autopurge.purgeInterval=1</span><br><span class="line"> </span><br><span class="line">8、保存并关闭 zoo.cfg 文件:</span><br><span class="line">    </span><br><span class="line">9、进入到 /usr/local/services/zookeeper/zookeeper-3.4.9/bin 目录中：</span><br><span class="line">    cd ../bin/</span><br><span class="line"> </span><br><span class="line">10、用 vim 打开 /etc/ 目录下的配置文件 profile：</span><br><span class="line">    vim /etc/profile</span><br><span class="line">    并在其尾部追加如下内容：</span><br><span class="line"> </span><br><span class="line">    # idea - zookeeper-3.4.9 config start - 2016-09-08</span><br><span class="line"> </span><br><span class="line">    export ZOOKEEPER_HOME=/usr/local/services/zookeeper/zookeeper-3.4.9/</span><br><span class="line">    export PATH=$ZOOKEEPER_HOME/bin:$PATH</span><br><span class="line">    export PATH</span><br><span class="line"> </span><br><span class="line">    # idea - zookeeper-3.4.9 config start - 2016-09-08</span><br><span class="line"> </span><br><span class="line">11、使 /etc/ 目录下的 profile 文件即可生效：</span><br><span class="line">    source /etc/profile</span><br><span class="line"> </span><br><span class="line">12、启动 zookeeper 服务：</span><br><span class="line">    zkServer.sh start</span><br><span class="line">    如打印如下信息则表明启动成功：</span><br><span class="line">    ZooKeeper JMX enabled by default</span><br><span class="line">    Using config: /usr/local/services/zookeeper/zookeeper-3.4.9/bin/../conf/zoo.cfg</span><br><span class="line">    Starting zookeeper ... STARTED</span><br><span class="line"> </span><br><span class="line">13、查询 zookeeper 状态：</span><br><span class="line">    zkServer.sh status</span><br><span class="line"> </span><br><span class="line">14、关闭 zookeeper 服务：</span><br><span class="line">    zkServer.sh stop</span><br><span class="line">    如打印如下信息则表明成功关闭：</span><br><span class="line">    ZooKeeper JMX enabled by default</span><br><span class="line">    Using config: /usr/local/services/zookeeper/zookeeper-3.4.9/bin/../conf/zoo.cfg</span><br><span class="line">    Stopping zookeeper ... STOPPED</span><br><span class="line"> </span><br><span class="line">15、重启 zookeeper 服务：</span><br><span class="line">    zkServer.sh restart</span><br><span class="line">    如打印如下信息则表明重启成功：</span><br><span class="line">    ZooKeeper JMX enabled by default</span><br><span class="line">    Using config: /usr/local/services/zookeeper/zookeeper-3.4.9/bin/../conf/zoo.cfg</span><br><span class="line">    ZooKeeper JMX enabled by default</span><br><span class="line">    Using config: /usr/local/services/zookeeper/zookeeper-3.4.9/bin/../conf/zoo.cfg</span><br><span class="line">    Stopping zookeeper ... STOPPED</span><br><span class="line">    ZooKeeper JMX enabled by default</span><br><span class="line">    Using config: /usr/local/services/zookeeper/zookeeper-3.4.9/bin/../conf/zoo.cfg</span><br><span class="line">    Starting zookeeper ... STARTED</span><br></pre></td></tr></table></figure>
<h6 id="Kafka"><a href="#Kafka" class="headerlink" title="Kafka"></a>Kafka</h6><hr>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">  1、将包下载到相关的目录，然后解压Zookeeper到指定目录；</span><br><span class="line">    #cd /opt;</span><br><span class="line">    #tar -zxvf kafka_2.10-0.10.1.1.tgz;</span><br><span class="line">    </span><br><span class="line">  2、配置日志：进入到/opt/kafka_2.10-0.10.1.1目录创建日志目录；</span><br><span class="line">    #cd /opt/kafka_2.10-0.10.1.1；</span><br><span class="line">    #mkdir logs；</span><br><span class="line">    </span><br><span class="line">3、修改kafka配置文件；</span><br><span class="line">    进入到/opt/kafka_2.10-0.10.1.1/config目录，修改server.properties。</span><br><span class="line">                       #cd /opt/kafka_2.10-0.10.1.1/config；</span><br><span class="line">                       #vi server.properties #编辑修改相应的参数</span><br><span class="line">                          broker.id=0</span><br><span class="line">                          port=9092 #端口号</span><br><span class="line">                          host.name=127.0.0.1 #服务器IP地址，修改为自己的服务器IP</span><br><span class="line">                          log.dirs=/opt/kafka_2.10-0.10.1.1/logs #日志存放路径，上面创建的目录</span><br><span class="line">                          zookeeper.connect=localhost:2181 #zookeeper地址和端口，单机配置部署，localhost:2181</span><br><span class="line">                       #:wq! #保存退出</span><br><span class="line">                  4、启动Zookeeper：</span><br><span class="line">                       Kafka支持内置的Zookeeper和引用外部的Zookeeper，本文使用外部的Zookeeper。</span><br><span class="line">                       启动Zookeeper可以参考前面文章： Linux下Zookeeper的（单机）安装与简单测试。</span><br><span class="line">                  5、Kafka操作。</span><br><span class="line">                       （1）、启动kafka命令：</span><br><span class="line">                                #cd /opt/kafka_2.10-0.10.1.1/bin;</span><br><span class="line">                                # ./kafka-server-start.sh /opt/kafka_2.10-0.10.1.1/config/server.properties &amp;;</span><br><span class="line">                       （2）、停止kafka命令：</span><br><span class="line">                                # ./kafka-server-stop.sh</span><br><span class="line">                       （3）、创建Topic：(创建一个名为test的topic，只有一个副本，一个分区。)</span><br><span class="line">                                #./kafka-topics.sh --create --zookeeper 127.0.0.1:2181 --replication-factor 1 --partitions 1 --topic test</span><br><span class="line">                       （4）、列出所有Topic：</span><br><span class="line">                                #./kafka-topics.sh -list -zookeeper 127.0.0.1:2181                  </span><br><span class="line">                       （5）、启动Producer并发送消息：</span><br><span class="line">                                #./kafka-console-producer.sh --broker-list localhost:9092 --topic test</span><br><span class="line">                               （输入相应的消息，eg:hello kafka;按Ctrl+C结束）</span><br><span class="line">                        （6）、启动Consumer并接收消息：</span><br><span class="line">                                #./kafka-console-consumer.sh --zookeeper 127.0.0.1:2181 --topic test --from-beginning</span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">kafka进程总是在启动一段时间后自动停止:解决                            </span><br><span class="line">bin/kafka-server-start.sh -daemon ./config/server.properties</span><br></pre></td></tr></table></figure>
<h6 id="ELK"><a href="#ELK" class="headerlink" title="ELK:"></a>ELK:</h6><ul>
<li>elasticsearch : <code>9200</code><ul>
<li>tar 解压;  </li>
<li>修改/config/jvm.options 中的内存为100M</li>
<li>bin / sh elasticsearch</li>
<li>安装IK分词器</li>
</ul>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">1.download or compile</span><br><span class="line"></span><br><span class="line">-   optional 1 - download pre-build package from here: https://github.com/medcl/elasticsearch-analysis-ik/releases</span><br><span class="line"></span><br><span class="line">unzip plugin to folder your-es-root/plugins/</span><br><span class="line"></span><br><span class="line">-   optional 2 - use elasticsearch-plugin to install ( version &gt; v5.5.1 ):</span><br><span class="line"></span><br><span class="line">./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.3/elasticsearch-analysis-ik-6.2.3.zip</span><br><span class="line"></span><br><span class="line">2.restart elasticsearch</span><br><span class="line">Quick Example</span><br><span class="line"></span><br><span class="line">1.create a index</span><br><span class="line"></span><br><span class="line">curl -XPUT http://localhost:9200/index</span><br><span class="line"></span><br><span class="line">2.create a mapping</span><br><span class="line"></span><br><span class="line">curl -XPOST http://localhost:9200/index/fulltext/_mapping -H &apos;Content-Type:application/json&apos; -d&apos;</span><br><span class="line">&#123;</span><br><span class="line">        &quot;properties&quot;: &#123;</span><br><span class="line">            &quot;content&quot;: &#123;</span><br><span class="line">                &quot;type&quot;: &quot;text&quot;,</span><br><span class="line">                &quot;analyzer&quot;: &quot;ik_max_word&quot;,</span><br><span class="line">                &quot;search_analyzer&quot;: &quot;ik_max_word&quot;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">&#125;&apos;</span><br></pre></td></tr></table></figure>
<ul>
<li>kibana: <code>5601</code><ul>
<li>tar 解压;</li>
<li>sh kibana</li>
</ul>
</li>
<li>logstash :  <code>9600</code><ul>
<li>tar 解压;</li>
<li>修改/config/jvm.options 中的内存为100M</li>
<li>bin/logstash -e ‘input { stdin { } } output { stdout {} }’</li>
</ul>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">启动命令：</span><br><span class="line">cd bin</span><br><span class="line">./logstash -e &apos;input &#123; stdin &#123; &#125; &#125; output &#123; stdout &#123;&#125; &#125;&apos;</span><br></pre></td></tr></table></figure>
<ul>
<li>如果出现以下代码表示启动成功：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Sending Logstash&apos;s logs to /home/rzxes/logstash-5.3.1/logs which is now configured via log4j2.properties</span><br><span class="line">[2017-05-16T10:27:36,957][INFO ][logstash.setting.writabledirectory] Creating directory &#123;:setting=&gt;&quot;path.queue&quot;, :path=&gt;&quot;/home/rzxes/logstash-5.3.1/data/queue&quot;&#125;</span><br><span class="line">[2017-05-16T10:27:37,041][INFO ][logstash.agent           ] No persistent UUID file found. Generating new UUID &#123;:uuid=&gt;&quot;c987803c-9b18-4395-bbee-a83a90e6ea60&quot;, :path=&gt;&quot;/home/rzxes/logstash-5.3.1/data/uuid&quot;&#125;</span><br><span class="line">[2017-05-16T10:27:37,581][INFO ][logstash.pipeline        ] Starting pipeline &#123;&quot;id&quot;=&gt;&quot;main&quot;, &quot;pipeline.workers&quot;=&gt;1, &quot;pipeline.batch.size&quot;=&gt;125, &quot;pipeline.batch.delay&quot;=&gt;5, &quot;pipeline.max_inflight&quot;=&gt;125&#125;</span><br><span class="line">[2017-05-16T10:27:37,682][INFO ][logstash.pipeline        ] Pipeline main started</span><br><span class="line">The stdin plugin is now waiting for input:</span><br><span class="line">[2017-05-16T10:27:37,886][INFO ][logstash.agent           ] Successfully started Logstash API endpoint &#123;:port=&gt;9600&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>Logstash连接mysql作为数据源，ES作为数据输出端：<ul>
<li>在Logstash的根目录新建文件xxx.conf;eg:test.conf;里面的内容为：</li>
</ul>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">input &#123;</span><br><span class="line">    stdin &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    jdbc &#123;</span><br><span class="line">      # 数据库地址  端口  数据库名</span><br><span class="line">      jdbc_connection_string =&gt; &quot;jdbc:mysql://127.0.0.1:3306/mamabuy-trade&quot;</span><br><span class="line">      # 数据库用户名</span><br><span class="line">      jdbc_user =&gt; &quot;root&quot;</span><br><span class="line">      # 数据库密码</span><br><span class="line">      jdbc_password =&gt; &quot;war3sxkcs&quot;</span><br><span class="line">      # mysql java驱动地址</span><br><span class="line">      jdbc_driver_library =&gt; &quot;/usr/local/software/mysql-connector-java-5.1.44.jar&quot;</span><br><span class="line">      jdbc_driver_class =&gt; &quot;com.mysql.jdbc.Driver&quot;</span><br><span class="line">      jdbc_paging_enabled =&gt; &quot;true&quot;</span><br><span class="line">      jdbc_page_size =&gt; &quot;100000&quot;</span><br><span class="line">      # sql 语句文件，也可以直接写SQL，如statement =&gt; &quot;select * from table1&quot;</span><br><span class="line">      statement_filepath =&gt; &quot;/usr/local/elk/logstash/test.sql&quot;</span><br><span class="line">      schedule =&gt; &quot;* * * * *&quot;</span><br><span class="line">      type =&gt; &quot;jdbc&quot;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">output &#123;</span><br><span class="line">    stdout &#123;</span><br><span class="line">        codec =&gt; json_lines</span><br><span class="line">    &#125;</span><br><span class="line">    elasticsearch &#123;</span><br><span class="line">        hosts  =&gt; &quot;127.0.0.1:9200&quot;</span><br><span class="line">        index =&gt; &quot;test-1&quot; #索引名称</span><br><span class="line">        document_type =&gt; &quot;form&quot; #type名称</span><br><span class="line">        document_id =&gt; &quot;%&#123;id&#125;&quot; #id必须是待查询的数据表的序列字段</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>并在logstash的根目录下新建文件<code>test.sql</code>;</li>
<li><code>test.sql</code>文件里面的内容为一条查询sql语句,eg:</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">1. eg:</span><br><span class="line">select * from table1</span><br><span class="line">2. 初次启动用全量同步：</span><br><span class="line">select id,category_id,brand_id,spu_name,price,status from t_product</span><br><span class="line">3. 下次启动用增量同步</span><br><span class="line">select id,category_id,brand_id,spu_name,price,status from t_product where update_time &gt; :sql_lastvalue</span><br></pre></td></tr></table></figure>
<ul>
<li>注意：<code>test.conf</code>和<code>test.sql</code>两个文件都在logstash的根目录下</li>
<li>此时再启动logstash:</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin/logstash -f test.conf</span><br></pre></td></tr></table></figure>
<p><img src="https://sxk.s3.didiyunapi.com/pic/elk/1.jpg" alt="image"></p>
<ul>
<li>此时mysql中(test.conf配置)的数据库已经连接,test.sql里面的sql语句被执行;logstash从mysql里面查询值返回到ElasticSearch可以在kibana中可视化出来</li>
</ul>
<h5 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h5><hr>
<p>1.安装gcc gcc-c++(如新环境,未安装请先安装)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install -y gcc gcc-c++</span><br></pre></td></tr></table></figure>
<p>2.安装PCRE库</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ cd /usr/local/</span><br><span class="line">$ wget http://jaist.dl.sourceforge.net/project/pcre/pcre/8.33/pcre-8.33.tar.gz</span><br><span class="line">$ tar -zxvf pcre-8.36.tar.gz</span><br><span class="line">$ cd pcre-8.36</span><br><span class="line">$ ./configure</span><br><span class="line">$ make &amp;&amp; make install</span><br><span class="line"></span><br><span class="line">或者：yum install -y pcre pcre-devel</span><br></pre></td></tr></table></figure>
<p>3.安装SSL库</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ cd /usr/local/</span><br><span class="line">$ wget http://www.openssl.org/source/openssl-1.0.1j.tar.gz</span><br><span class="line">$ tar -zxvf openssl-1.0.1j.tar.gz</span><br><span class="line">$ cd openssl-1.0.1j</span><br><span class="line">$ ./config</span><br><span class="line">$ make &amp;&amp; make install</span><br><span class="line"></span><br><span class="line">或者：yum install -y openssl openssl-devel</span><br></pre></td></tr></table></figure>
<p>4.安装zlib库存</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ cd /usr/local/</span><br><span class="line">$ wget http://zlib.net/zlib-1.2.11.tar.gz</span><br><span class="line">$ tar -zxvf zlib-1.2.11.tar.gz</span><br><span class="line">$ ./configure</span><br><span class="line">$ make &amp;&amp; make install</span><br><span class="line"></span><br><span class="line">或者：yum install -y zlib zlib-devel</span><br></pre></td></tr></table></figure>
<p>5.安装nginx</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">$ cd /usr/local/</span><br><span class="line">$ wget -c https://nginx.org/download/nginx-1.10.1.tar.gz</span><br><span class="line">$ tar -zxvf nginx-1.10.0.tar.gz</span><br><span class="line">$ cd nginx-1.10.0 </span><br><span class="line">$ 建议：./configure</span><br><span class="line">$ 不建议：./configure --user=nobody --group=nobody --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_gzip_static_module --with-http_realip_module --with-http_sub_module --with-http_ssl_module</span><br><span class="line">(注: --with-http_ssl_module:这个不加后面在nginx.conf配置ssl:on后,启动会报nginx: [emerg] unknown directive &quot;ssl&quot; in /opt/nginx/conf/nginx.conf 异常)</span><br><span class="line">$ make &amp;&amp; make install</span><br><span class="line"></span><br><span class="line">报错:./configure: error: the HTTP gzip module requires the zlib library</span><br><span class="line"></span><br><span class="line">在–prefix后面接以下命令:</span><br><span class="line"></span><br><span class="line">--with-pcre=/usr/local/pcre-8.36 指的是pcre-8.36 的源码路径。--with-zlib=/usr/local/zlib-1.2.8 指的是zlib-1.2.8 的源码路径。</span><br></pre></td></tr></table></figure>
<h6 id="SSL"><a href="#SSL" class="headerlink" title="SSL"></a>SSL</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">upstream smallkk &#123;</span><br><span class="line">    server 127.0.0.1:8080;</span><br><span class="line">&#125;</span><br><span class="line">server &#123;</span><br><span class="line">    listen 80;</span><br><span class="line">    server_name www.s-name.com;</span><br><span class="line">    rewrite ^(.*) https://$host$1 permanent;</span><br><span class="line">&#125;</span><br><span class="line">server &#123;</span><br><span class="line">    listen 443 ssl;</span><br><span class="line">    server_name www.s-name.com;</span><br><span class="line">    ssl on;</span><br><span class="line">    ssl_certificate 1_s-name.com_bundle.crt;</span><br><span class="line">    ssl_certificate_key 2_s-name.com.key;</span><br><span class="line">    ssl_session_timeout 5m;</span><br><span class="line">    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;</span><br><span class="line">    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;</span><br><span class="line">    ssl_prefer_server_ciphers on;</span><br><span class="line">    location / &#123;</span><br><span class="line">        root html;</span><br><span class="line">        index index.html index.htm;</span><br><span class="line">        proxy_pass http://smallkk;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>6.启动</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ /usr/local/nginx/sbin/nginx</span><br><span class="line">or</span><br><span class="line">[root@localhost sbin]# ./nginx    -c /usr/local/nginx-1.10.1/conf/nginx.conf </span><br><span class="line">&lt;!--指定下位置再启动--&gt;</span><br></pre></td></tr></table></figure>
<p>7.重启</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ /usr/local/nginx/sbin/nginx –s reload</span><br></pre></td></tr></table></figure>
<p>8.停止</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ /usr/local/nginx/sbin/nginx –s stop</span><br></pre></td></tr></table></figure>
<p>9.测试配置文件是否正常</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ /usr/local/nginx/sbin/nginx –t</span><br></pre></td></tr></table></figure>
<p>10.强制关闭</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pkill nginx</span><br></pre></td></tr></table></figure>
<h6 id="配置："><a href="#配置：" class="headerlink" title="配置："></a>配置：</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br></pre></td><td class="code"><pre><span class="line">Server</span><br><span class="line"></span><br><span class="line">接收请求的服务器需要将不同的请求按规则转发到不同的后端服务器上，在 nginx 中我们可以通过构建虚拟主机（server）的概念来将这些不同的服务配置隔离。</span><br><span class="line"></span><br><span class="line">server &#123;</span><br><span class="line">listen 80;</span><br><span class="line">server_name localhost;</span><br><span class="line">root html;</span><br><span class="line">index index.html index.htm;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">例如我们笔戈玩下的两个子项目 passport 和 wan 就可以通过在 nginx 的配置文件中配置两个 server，servername 分别为 passport.bigertech.com 和 wan.bigertech.com。这样的话不同的 url 请求就会对应到 nginx 相应的设置，转发到不同的后端服务器上。</span><br><span class="line">这里的 listen 指监听端口，server_name 用来指定IP或域名，多个域名对应统一规则可以空格分开，index 用于设定访问的默认首页地址，root 指令用于指定虚拟主机的网页跟目录，这个地方可以是相对地址也可以是绝对地址。</span><br><span class="line">通常情况下我们可以在 nginx.conf 中配置多个server，对不同的请求进行设置。就像这样：</span><br><span class="line"></span><br><span class="line">server &#123;</span><br><span class="line">listen 80;</span><br><span class="line">server_name host1;</span><br><span class="line">root html;</span><br><span class="line">index index.html</span><br><span class="line">index.htm;</span><br><span class="line">&#125;</span><br><span class="line">server &#123;</span><br><span class="line">listen 80;</span><br><span class="line">server_name host2;</span><br><span class="line">root /data/www/html;</span><br><span class="line">index index.html index.htm;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">但是当 server 超过2个时，建议将不同对虚拟主机的配置放在另一个文件中，然后通过在主配置文件 nginx.conf 加上 include 指令包含进来。更便于管理。</span><br><span class="line"></span><br><span class="line">include vhosts/*.conf;</span><br><span class="line"></span><br><span class="line">就可以把vhosts的文件都包含进去啦。</span><br><span class="line"></span><br><span class="line">Localtion</span><br><span class="line">每个 url 请求都会对应的一个服务，nginx 进行处理转发或者是本地的一个文件路径，或者是其他服务器的一个服务路径。而这个路径的匹配是通过 location 来进行的。我们可以将 server 当做对应一个域名进行的配置，而 location 是在一个域名下对更精细的路径进行配置。</span><br><span class="line"></span><br><span class="line">以上面的例子，可以将root和index指令放到一个location中，那么只有在匹配到这个location时才会访问root后的内容：</span><br><span class="line"></span><br><span class="line">location / &#123;</span><br><span class="line">   root /data/www/host2; </span><br><span class="line">   index index.html index.htm; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location 匹配规则</span><br><span class="line"></span><br><span class="line">~ 波浪线表示执行一个正则匹配，区分大小写</span><br><span class="line">~* 表示执行一个正则匹配，不区分大小写</span><br><span class="line">^~ ^~表示普通字符匹配，如果该选项匹配，只匹配该选项，不匹配别的选项，一般用来匹配目录</span><br><span class="line">= 进行普通字符精确匹配</span><br><span class="line"></span><br><span class="line">匹配例子：</span><br><span class="line"></span><br><span class="line">location = / &#123;</span><br><span class="line"># 只匹配&quot;/&quot;. [ configuration A ]</span><br><span class="line">&#125;</span><br><span class="line">location / &#123;</span><br><span class="line"># 匹配任何请求，因为所有请求都是以&quot;/&quot;开始 # 但是更长字符匹配或 者正则表达式匹配会优先匹配 [ configuration B ]</span><br><span class="line">&#125;</span><br><span class="line">location ^~ /images/ &#123;</span><br><span class="line"># 匹配任何以 /images/ 开始的请求，并停止匹配 其它</span><br><span class="line">location [ configuration C ]</span><br><span class="line">&#125;</span><br><span class="line">location ~* .(gif|jpg|jpeg)$ &#123;</span><br><span class="line"># 匹配以 gif, jpg, or jpeg结尾的请求.</span><br><span class="line"># 但是所有 /images/ 目录的请求将由 [Configuration C]处理.</span><br><span class="line">[ configuration D ]</span><br><span class="line">&#125;</span><br><span class="line">请求:/ -&gt; 符合configuration A</span><br><span class="line">/documents/document.html -&gt; 符合configuration B</span><br><span class="line">/images/1.gif -&gt; 符合configuration C</span><br><span class="line">/documents/1.jpg -&gt;符合 configuration D</span><br><span class="line"></span><br><span class="line">静态文件映射</span><br><span class="line">访问文件的配置主要有 root 和 aliasp’s 两个指令。这两个指令的区别容易弄混：</span><br><span class="line">alias</span><br><span class="line">alias后跟的指定目录是准确的，并且末尾必须加 /。</span><br><span class="line"></span><br><span class="line">location /c/ &#123;</span><br><span class="line">alias /a/;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">root</span><br><span class="line">root后跟的指定目录是上级目录，并且该上级目录下要含有和location后指定名称的同名目录才行。</span><br><span class="line"></span><br><span class="line">location /c/ &#123;</span><br><span class="line">root /a/;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">如果你需要将这个目录展开，在这个location的末尾加上「autoindex on; 」就可以了</span><br><span class="line"></span><br><span class="line">转发</span><br><span class="line">配置起来很简单比如我要将所有的请求到转移到真正提供服务的一台机器的 8001 端口，只要这样：</span><br><span class="line"></span><br><span class="line">location / &#123;</span><br><span class="line">proxy_pass 172.16.1.1:8001;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">这样访问host时，就都被转发到 172.16.1.1的8001端口去了。</span><br><span class="line"></span><br><span class="line">负载均衡</span><br><span class="line"></span><br><span class="line">upstream myserver; &#123;</span><br><span class="line">ip_hash;</span><br><span class="line">server 172.16.1.1:8001;</span><br><span class="line">server 172.16.1.2:8002;</span><br><span class="line">server 172.16.1.3;</span><br><span class="line">server 172.16.1.4;</span><br><span class="line">&#125;</span><br><span class="line">location / &#123;</span><br><span class="line">proxy_pass http://myserver;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">我们在 upstream 中指定了一组机器，并将这个组命名为 myserver，这样在 proxypass 中只要将请求转移到 myserver 这个 upstream 中我们就实现了在四台机器的反向代理加负载均衡。其中的 ip_hash 指明了我们均衡的方式是按照用户的 ip 地址进行分配。另外还有轮询、指定权重轮询、fair、url_hash几种调度算法。</span><br><span class="line"></span><br><span class="line">总结</span><br><span class="line"></span><br><span class="line">以上是最简单的通过 nginx 实现静态文件转发、反向代理和负载均衡的配置。在 nginx 中所有的功能都是通过模块来实现的，比如当我们配置 upstream 时是用 upstream 模块，而 server 和 location 是在 http core 模块，其他的还有流控的 limt 模块，邮件的 mail 模块，https 的 ssl 模块。他们的配置都是类似的可以再 nginx 的模块文档中找到详细的配置说明。</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/03/24/深入理解Java虚拟机/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/03/24/深入理解Java虚拟机/" itemprop="url">深入理解Java虚拟机</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-03-24T08:57:12+08:00">
                2018-03-24
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <blockquote>
<h2 id="深入理解Java虚拟机-jvm性能调优-内存模型-虚拟机原理"><a href="#深入理解Java虚拟机-jvm性能调优-内存模型-虚拟机原理" class="headerlink" title="深入理解Java虚拟机(jvm性能调优+内存模型+虚拟机原理)"></a><strong>深入理解Java虚拟机(jvm性能调优+内存模型+虚拟机原理)</strong></h2></blockquote>
<blockquote>
<p>@Author:<a href="mailto:songxiankun2008@126.com" target="_blank" rel="noopener">songxiankun2008@126.com</a></p>
</blockquote>
<h5 id="1-jdk-、jre-、jvm的关系"><a href="#1-jdk-、jre-、jvm的关系" class="headerlink" title="1. jdk 、jre 、jvm的关系"></a>1. jdk 、jre 、jvm的关系</h5><hr>
<p>== JDK &gt; JRE &gt; JVM ==</p>
<p>Java Development Kit  &gt; Java Runtime Environment  &gt;  Java Virtual Machine</p>
<p>(根据这个层级目录关系，无论安装JDK还是JRE,JVM都会随之安装的)</p>
<p>代码开发是在JDK，运行是在JRE，执行时的字节码在JVM</p>
<p>RIA:富客户端应用程序</p>
<p><img src="https://sxk.s3.didiyunapi.com/pic/jvm/1.png" alt="image"></p>
<h5 id="2-内存溢出问题的分析与解决"><a href="#2-内存溢出问题的分析与解决" class="headerlink" title="2. 内存溢出问题的分析与解决"></a>2. 内存溢出问题的分析与解决</h5><hr>
<p>VM arguments (添加Java虚拟机运行时的参数):</p>
<pre><code>-XX:+HeapDumpOnOutOfMemoryError
// 制定一个快照,堆内存出错,内存溢出等报错的时候
//快照会存放在项目里面后缀名为.hprof的文件

-XX:+HeapDumpOnOutOfMemoryError -Xms20m -Xmx20m
// 制定溢出内存的大小;超过就报错,停止执行并释放内存
</code></pre><p>hprof这个内存快照文件需要专门的工具打开：Eclipse Memory Analyzer(基于Eclipse内核),导入文件后可以看到快照的信息：图谱和错误分布</p>
<h5 id="3-jvm可视化监控工具"><a href="#3-jvm可视化监控工具" class="headerlink" title="3. jvm可视化监控工具"></a>3. jvm可视化监控工具</h5><hr>
<p>jdk安装目录下：</p>
<pre><code>\bin\jconsole.exe
</code></pre><p>如果配置Path变量路径的话直接</p>
<pre><code>cmd:jconsole
</code></pre><p>在cmd里面的<code>jps</code>命令是可以列出所有的java进程</p>
<p>在jconsole.exe里面堆内存图像曲线解释：类似心电图一上一下;问：为什么不是一直都在上? 答：上升是在耗费内存的使用,下降是垃圾回收机制及时的回收了垃圾</p>
<p>在内存池的”PS Eden Space”(Eden区)是GC经常光顾的地方,所以用可视化工具查看内存变化的区域就是多看这个Eden区域</p>
<p>堆内存分为：</p>
<pre><code>① 新生代内存 ② 老年代内存
</code></pre><p>堆内存又分为3个板块： </p>
<pre><code>① Eden(占2/4) ② Survivor(占1/4) ③ Survivor(占1/4)
</code></pre><p>刚刚new出来的一个对象首先是扔到Eden区域,GC会随时将这里面是垃圾的回收走,如果没有被回收到的就是存活下来的,就会进入Survivor(存活区)内存块里面;如果长期存活下来就会进入”老年代”内存区域.</p>
<h6 id="4-Java的发展历史"><a href="#4-Java的发展历史" class="headerlink" title="4. Java的发展历史"></a>4. Java的发展历史</h6><hr>
<ol>
<li><p>1995年5月 Oak语言 ==》 Java 1.0 (理念：write once and run anywhere 一次写下 到处运行)</p>
</li>
<li><p>1996年1月 jdk 1.0 发布; jvm在jdk之中,这是第一个jvm,是sun公司的Classic VM</p>
</li>
<li><p>1996年9月 首届 JavaOne 大会</p>
</li>
<li><p>1997年2月 jdk 1.1  发布;(内部类,反射,JDBC,JavaBeans,rmi,jar文件格式)</p>
</li>
<li><p>1998年    jdk 1.2 发布 此时Java就有了三个方向(J2SE J2EE J2ME),与此同时swing也加入了进去,并且内置了jit,改进了HotSpot VM</p>
</li>
<li><p>2000年5月 jdk 1.3 新增Timer 改进java2D;从此开始java进入了一个高速发展期,并规定每两年就发布一个java的主版本</p>
</li>
<li><p>2002年2月 jdk 1.4 发布,是一个里程碑的版本,非常核心,是Java走向成熟的版本;这个时候问世了==Struts,Hibernate,Spring1.x==,Spring1.X全都是基于”配置”执行,在jdk1.4上可以直接运行;与此新增 正则表达式,Nio,日志,Xml解析器</p>
</li>
<li><p>2004年9月 jdk 1.5 发布,别名:Tiger,内容:自动装箱拆箱、泛型、注解、枚举、变长参数、增强for循环;(因为有注解可以用了,所以Spring2.X摒弃了1.X的各种配置,开始使用jdk1.5的注解)</p>
</li>
<li><p>2006年 jdk 1.6  更名：将98年1.2版本的==J2SE-&gt;JavaSE、J2EE-&gt;JavaEE、J2ME-&gt;JavaME; jdk1.6 -&gt; JDK6.0== ;新特性:提供脚本语言的支持(动态语言)、提供了一些编译APi、一些微型服务http的APi</p>
<p>2006年11月13日Java大会宣布将所有的JDK什么东西的全部开源(耗费大量的人力财力,没有赶上两年一个jdk主版本的速度,所以下一个主版本的发布就在2009年)</p>
</li>
<li><p>2009年发布 jdk 1.7 (规划了 Lambda(动态语言/函数式编程),Jigsaw(模块化),但是大多数的模块化都是用的OSGI)</p>
<p>同年(2009)Oracle公司以74亿美元收购了SUN公司</p>
<p>从2009年开始发布jdk7的第一个版本,到2011年发布了JDK7的最终版本</p>
</li>
<li><p>2014年3月 JDK1.8发布</p>
</li>
</ol>
<h6 id="5-Java技术体系"><a href="#5-Java技术体系" class="headerlink" title="5. Java技术体系"></a>5. Java技术体系</h6><hr>
<p>  ①</p>
<ul>
<li>Java程序设计语言</li>
<li>各硬件平台上的Java虚拟机</li>
<li>Class文件格式</li>
<li>Java APi</li>
<li><p>第三方的Java类库</p>
<p>②</p>
</li>
<li>JavaSE   Java Standard edition 标准版</li>
<li>JavaEE   Java企业级开发：共有13种技术体系</li>
<li>JavaME   nokia s60 java</li>
</ul>
<h5 id="6-Java8新特性"><a href="#6-Java8新特性" class="headerlink" title="6. Java8新特性"></a>6. Java8新特性</h5><hr>
<ul>
<li><p>接口的默认方法和静态方法</p>
<ul>
<li>(Java8使用两个新概念扩展了接口的含义：默认方法和静态方法。默认方法是的开发者可以在不破坏二进制兼容性的前提下，往现存接口中添加新的方法，即不强制那些实现了该接口的类也同事实现这个新加的方法。)</li>
</ul>
</li>
<li><p>Lambda 表达式和函数式编程</p>
</li>
<li>Date APi</li>
<li><p>更好的类型推断</p>
<ul>
<li>(Java8编译器在类型推断方面有很大的提升，在很多场景下编译器可以推导出某个参数的数据类型，从而使得代码更为简洁)</li>
</ul>
</li>
<li><p>Nashorn JavaScript引擎</p>
<ul>
<li>(使用Metaspace（JEP 122）代替持久代（PermGen space）。在JVM参数方面，使用-XX:MetaSpaceSize和-XX：MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize)</li>
</ul>
</li>
</ul>
<h5 id="7-lanmbda表达式简介"><a href="#7-lanmbda表达式简介" class="headerlink" title="7. lanmbda表达式简介"></a>7. lanmbda表达式简介</h5><hr>
<p>不使用Lanmbda:</p>
<pre><code>jButton.addActionListener(new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent e){
        System.out.println(&quot;Hello&quot;);
    }
})
</code></pre><p>使用Lanmbda表达式：</p>
<pre><code>jButton.addActionListener(event -&gt; System.out.println(&quot;Hello&quot;));
</code></pre><h5 id="8-Java虚拟机"><a href="#8-Java虚拟机" class="headerlink" title="8. Java虚拟机"></a>8. Java虚拟机</h5><hr>
<ul>
<li><ol>
<li>classic vm</li>
</ol>
<ul>
<li>96年发布的虚拟机</li>
<li>世界上第一款商用的Java虚拟机</li>
<li>只能使用纯解释器的方式来执行Java代码</li>
</ul>
</li>
<li><ol start="2">
<li>Exact VM</li>
</ol>
<ul>
<li>Exact Memory Management 准确试内存管理</li>
<li>编译器和解释器混合工作以及两级即时编译器</li>
<li>只在Solaris平台发布<ul>
<li>(还没得Windows和Linux)</li>
</ul>
</li>
<li>英雄气短<ul>
<li>(jdk1.2后就没怎么用Exact VM了,反而还是classic vm,到了JDK1.3后换成了其他的)</li>
</ul>
</li>
</ul>
</li>
<li><ol start="3">
<li>==HotSpot VM==</li>
</ol>
<ul>
<li>HotSpot的历史<ul>
<li>HotSpot VM不是sun公司的,但是HotSpot VM继承sun公司前两款商用虚拟机的优点,于是sun公司就收购了HotSpot VM</li>
</ul>
</li>
<li>优势</li>
<li>称霸武林<ul>
<li>2006年宣布开源后,从2007年开始 sun公司下的两个不同的JDK的虚拟机全部都是HotSpot VM；从2007-至今都是这个虚拟机</li>
</ul>
</li>
</ul>
</li>
<li><ol start="4">
<li>KVM<ul>
<li>Kilobyte 简单、轻量、高度可移植</li>
<li>在手机平台运行(eg:noika s60 但是运行速度很慢)</li>
</ul>
</li>
</ol>
</li>
<li><ol start="5">
<li>JRockit<ul>
<li>BEA</li>
<li>世界上最快的Java虚拟机</li>
<li>专注于服务器端应用</li>
<li>优势<ul>
<li>垃圾收集器</li>
<li>MissionControl服务套件</li>
</ul>
</li>
</ul>
</li>
</ol>
</li>
<li><p>[ ]     BEA JRockit Mission Control,用来诊断泄露并指出根本原因。该工具的开销非常小，因此可以使用它来寻找生产环境中的系统的内存泄露。</p>
</li>
<li><p>[x]     BEA JRockit Mission Control (JRMC)于2005年12月面世，并从JRockit R26.0.0版本开始捆绑了这个工具套件，目前最新的版本是2.0.1.它是一组以极低的开销来监控、管理和分析生产环境中的应用程序的工具。它包括三个独立的应用程序：内存泄露检测器(Memory Leak Detector)、JVM运行时分析器(RunTime Analyzer)和管理控制台(Management Console)。</p>
</li>
<li><ol start="6">
<li>J9<ul>
<li>IBM公司出产,不是sun</li>
<li>原名为 Technology for Java Virtual Machine 简称：IT4J;但是为了方便阅读就取名为 J9</li>
<li>类似于HotSpot</li>
<li>从服务器端到桌面应用到嵌入</li>
</ul>
</li>
</ol>
</li>
<li><ol start="7">
<li>Dalvik<ul>
<li>谷歌的</li>
<li>安卓操作系统的重要组成部分</li>
<li>dex ( dalvik Executalbe )</li>
</ul>
</li>
</ol>
</li>
<li><ol start="8">
<li>Microsoft JVM<ul>
<li>在Java诞生初期,windows还是Java的铁杆粉丝,windows为了Java能在win下运行于是就有了这款Java虚拟机;这个虚拟机只能在windows环境下运行而且速度非常快!SUN公司不满意起诉Windows公司侵权以胜利告终;从此windows退出了JVM的研发。</li>
</ul>
</li>
</ol>
</li>
<li><ol start="9">
<li>Azul VM &amp; Liquid VM<ul>
<li>高性能的Java虚拟机</li>
<li>运行在该公司专有的硬件环境上超级炒鸡快</li>
<li>Liquid VM不需要操作系统的支持,因为它本身就是一个微型操作系统</li>
<li>平常说Java运行慢是因为程序执行会进入JVM，jvm再去调用操作系统，过程很长所以就慢；就完全体现出Liquid VM不要操作系统的优势;</li>
</ul>
</li>
</ol>
</li>
<li><ol start="10">
<li>Taobao VM<ul>
<li>淘宝根据Hotspot开发的国产的虚拟机</li>
<li>运行在 阿里 的服务器上性能非常高,但是自己拿过来用可能就达不到那样的效果</li>
<li>在GC的优化上非常高</li>
</ul>
</li>
</ol>
</li>
</ul>
<h5 id="9-内存区域"><a href="#9-内存区域" class="headerlink" title="9. 内存区域"></a>9. 内存区域</h5><hr>
<ol>
<li><p>运行时数据区里面包括”线程共享区”和”线程独占区”;</p>
</li>
<li><p>线程共享区里面又包括”方法区”和”Java堆”</p>
</li>
<li><p>线程独占区里面又包括”虚拟机栈”、”本地方法栈”和”程序计数器”</p>
</li>
<li><p>方法区：存储运行时常量池、已经虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据</p>
</li>
</ol>
<p>运行时数据区</p>
<ul>
<li><p><img src="https://sxk.s3.didiyunapi.com/pic/jvm/2.png" alt="image"></p>
</li>
</ul>
<p>++1. Java堆：存储对象实例++</p>
<p>++2. 虚拟机栈：存放方法运行时所需的数据，成为栈帧++</p>
<p>++3. 本地方法栈：为JVM所调用到的Nativa即本地方法服务++</p>
<p>++4. 程序计数器： 记录当前线程所执行到的字节码的行号++</p>
<ul>
<li><ol>
<li>程序计数器<ul>
<li><ol>
<li>程序计数器是一块较小的内存空间，它可以看做是当前线程所执行的字节码的行号指示器</li>
</ol>
</li>
<li><ol start="2">
<li>程序计数器处于线程独占区</li>
</ol>
</li>
<li><ol start="3">
<li>如果线程执行的是Java方法，这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是Native方法，这个计数器的值为undefined</li>
</ol>
</li>
<li><ol start="4">
<li>此区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域</li>
</ol>
</li>
<li><ol start="5">
<li>goto ,Java中的goto就是跳转到哪一行去执行;但是goto是保留字关键字,JDK官方没有用,开发者也不能用;即当前JDK版本不能用goto,那么可能JDK之后的版本才会允许使用goto或者赋予其他语法和含义</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="2">
<li>==Java虚拟机栈==<ul>
<li><ol>
<li>虚拟机栈描述的是JAVA方法执行的动态内存模型</li>
</ol>
</li>
<li><ol start="2">
<li>栈帧<ul>
<li><ol>
<li>每个方法执行,都会创建一个栈帧,伴随着方法从创建到执行完成。用于存储局部变量表,操作数栈,动态链接,方法出口等。</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="3">
<li>局部变量表<ul>
<li><ol>
<li>存放编译器可知的各种基本数据类型,引用类型,returnAddress类型</li>
</ol>
</li>
<li><ol start="2">
<li>局部变量表的内存空间在编译期完成分配,当进入一个方法时,这个方法需要在帧分配多少内存是固定的,在方法运行期间是不会改变局部变量表的大小</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="4">
<li>大小<ul>
<li><ol>
<li>StackOverflowError</li>
</ol>
</li>
<li><ol start="2">
<li>OutOfMemory</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="3">
<li>本地方法栈<ul>
<li>两个栈的区别 <ul>
<li>虚拟机栈 ：  为虚拟机执行Java方法服务</li>
<li>本地方法栈 ：为虚拟机执行native方法服务</li>
</ul>
</li>
<li>其实这个Java虚拟机栈和本地方法栈没有多大的区别,异同都有;</li>
</ul>
</li>
</ol>
</li>
<li><ol start="4">
<li>堆内存(Java堆)<ul>
<li><ol>
<li>存储对象的实例</li>
</ol>
</li>
<li><ol start="2">
<li>垃圾收集器管理的主要区域</li>
</ol>
</li>
<li><ol start="3">
<li>新生代、老年代、Eden空间<br>(分出来这那么多块啊空间的主要是为了垃圾回收)</li>
</ol>
</li>
<li><ol start="4">
<li>OutOfMemory</li>
</ol>
</li>
<li><ol start="5">
<li>用 -Xmx -Xms 来指定内存大小</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="5">
<li>方法区<ul>
<li><ol>
<li>存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据<ul>
<li><ol>
<li>类的信息：</li>
</ol>
<ul>
<li><ol>
<li>类的版本</li>
</ol>
</li>
<li><ol start="2">
<li>字段</li>
</ol>
</li>
<li><ol start="3">
<li>方法</li>
</ol>
</li>
<li><ol start="4">
<li>接口</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="2">
<li>方法区和永久代</li>
</ol>
</li>
<li><ol start="3">
<li>垃圾回收再方法区的行为</li>
</ol>
</li>
<li><ol start="4">
<li>异常的定义<ul>
<li><ol>
<li>OutOfMemoryError</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="6">
<li>运行时常量池<ul>
<li><ol>
<li>属于方法区里面的一块</li>
</ol>
</li>
<li><ol start="2">
<li>eg:</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
</ul>
<pre><code>String s1 = &quot;abc&quot;;
String s2 = &quot;abc&quot;;
System.out.print( s1 == s2 );
// true
// 此处的&quot;abc&quot;称之为 字节码常量
</code></pre><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">graph LR</span><br><span class="line">s1--&gt;abc</span><br><span class="line">s2--&gt;abc</span><br></pre></td></tr></table></figure>
<ul>
<li style="list-style: none"><input type="checkbox" checked> 字符串并不是在 堆空间 中new(此处并没有new)出一个实例;而是在“常量池”里面创建(不是new)出来的,常量池里面对字符串来说有专门的一个空间叫做StringTable,在这个字符串表里面的数据结构是HashSet(无序且不重复),所以相同的字符串进来的时候只会有一个地址,所以不同的变量是引用的这同一个地址</li>
</ul>
<pre><code>String s3 = new String(&quot;abc&quot;);
System.out.print( s1 == s3 );
//false
</code></pre><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">graph LR</span><br><span class="line">s1--&gt;常量池创建出来的abc</span><br><span class="line">s3--&gt;堆空间中new出来的abc</span><br></pre></td></tr></table></figure>
<ul>
<li style="list-style: none"><input type="checkbox" checked> 此处为“new”,上面的s1,s2并没有new是在常量池;此处的new说明是在堆内存中开辟了一个”abc”的空间被栈中的s3引用;因为s3引用的”abc”是在堆空间中,而s1||s2引用的”abc”是在常量池中,这两者的内存区域都不同,所以为false</li>
</ul>
<pre><code>System.out.print( s1 == s3.intern());
//true
//此处的s3.intern();即经过intern()方法的&quot;abc&quot; 称之为 运行时常量
</code></pre><ul>
<li><p>[x] 为什么此处 s1 == s3.intern()  ??; intern()这个方法的作用是将 在堆内存中new出来的字符串 搬到 常量池中;那么此时的s1和s3.intern();就都在常量池中,因为字符串的内容相同那么在常量池里面的地址就是同一个地址,无序不重复;</p>
</li>
<li><ol start="7">
<li>直接内存<ul>
<li><ol>
<li>并不是运行时数据区的一部分;也不是java虚拟机规范的一块区域</li>
</ol>
</li>
<li><ol start="2">
<li>但是又确确实实的是一块内存区域而且还在被频繁的使用</li>
</ol>
</li>
<li><ol start="3">
<li>直接内存不会受到java虚拟机内存的制约</li>
</ol>
</li>
<li><ol start="4">
<li>但是会受到系统物理内存的制约</li>
</ol>
</li>
<li><ol start="5">
<li>主要是在JDK1.4开始的Nio里面直接分配内存</li>
</ol>
</li>
<li><ol start="6">
<li>基于通道与缓冲区什么东西的 new inputOut…来分配堆外内存<ul>
<li>下面这两句不知道哪句是真的:<ul>
<li><ol>
<li>(在这一块区域如果申请不到内存的话也会抛出OutOfMemory这个异常)</li>
</ol>
</li>
<li><ol start="2">
<li>(当出现OutOfMemory的时候就可以考虑这个直接内存的使用)</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ol>
</li>
<li>并 没 有 搞 懂 </li>
</ul>
</li>
</ol>
</li>
</ul>
<h4 id="10-对象的创建"><a href="#10-对象的创建" class="headerlink" title="10. 对象的创建"></a>10. 对象的创建</h4><hr>
<ul>
<li><p>10.1.1  给对象分配内存（概述与顺序）</p>
<ul>
<li><ol>
<li>线程安全性问题</li>
</ol>
</li>
<li><ol start="2">
<li>初始化对象</li>
</ol>
</li>
<li><ol start="3">
<li>执行构造方法</li>
</ol>
</li>
<li><ol start="4">
<li>顺序：<ul>
<li><ol>
<li>new 类名</li>
</ol>
</li>
<li><ol start="2">
<li>根据new的参数在常量池中定位一个类的符号引用</li>
</ol>
</li>
<li><ol start="3">
<li>如果没有找到这个符号引用,说明类还没有加载;则进行类的加载,解析和初始化</li>
</ol>
</li>
<li><ol start="4">
<li>虚拟机为对象分配内存 （位于堆中）</li>
</ol>
</li>
<li><ol start="5">
<li>将分配的内存初始化为零（不包括对象头）</li>
</ol>
</li>
<li><ol start="6">
<li>调用对象的<code>init();</code>方法</li>
</ol>
</li>
<li>中间的那 4个步骤 是在jvm里面执行对用户屏蔽了的</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
<li><p>10.1.2 给对象分配内存（操作方法）</p>
<ul>
<li><ol>
<li>方法一： 指针碰撞<ul>
<li><ol>
<li>(将一个长方体分成两块,左边为free右边为used,让右侧使用的内存越来越大的时候左侧自然会越来越小)</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="2">
<li>方法二： 空间列表<ul>
<li><ol>
<li>(如果这个长方体的两侧已经发生混乱即两侧都有已经使用和未使用的内存这样不便于管理的时候,就会出现一个新的区域叫做空间列表;”空间列表”区域里面就会记载长方体左右两则的哪些空间是已经使用的哪些未被使用)</li>
</ol>
</li>
<li><ol start="2">
<li>指针碰撞 与 空间列表 具体在哪种情况下使用需要看”垃圾回收器”的情况</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
<li><p>10.2 线程安全性问题：</p>
<ul>
<li><ol>
<li>解释：在”指针碰撞”方法中如果在高并发情况下同时多个使用多个多个释放,此时将此长方体一分为二的”分界指针”来不及移动就会造成数据错误;同样在空间列表方法中,在空间列表区域里面的记载的数据来不及更新瞬间的使用与释放就也会出问题。</li>
</ol>
</li>
<li><ol start="2">
<li>方法一：线程同步</li>
</ol>
</li>
<li><ol start="3">
<li>方法二：本地线程分配缓冲<ul>
<li><ol>
<li>(有多少个线程,就在即将要使用到空闲内存区域中分配几个单独的小区域,每个线程对应一个小区域这样就不会有相互干涉;如果线程所在的这个区域的内存已经满了那么就再来分配一个小区域供这个线程使用;这种方法解决了方法加锁在效率上的问题)</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
<li>10.3 初始化对象</li>
<li>10.4 执行构造方法</li>
</ul>
<h5 id="11-探究-对象的结构"><a href="#11-探究-对象的结构" class="headerlink" title="11. 探究==对象的结构=="></a>11. 探究==对象的结构==</h5><hr>
<ul>
<li><ol>
<li>Header(对象头,对象的原数据)(这个是HotSpot虚拟机的对象头)<ul>
<li><ol>
<li>自身运行时数据(别名:Mark Word 根据32/64bit虚拟机的不同所占的大小来分为32/64位;将32位进行拆分,可能每几位就是一块,每一块都有不同的事做)<ul>
<li><ol>
<li>哈希值</li>
</ol>
</li>
<li><ol start="2">
<li>GC分代年龄</li>
</ol>
</li>
<li><ol start="3">
<li>锁状态标志</li>
</ol>
</li>
<li><ol start="4">
<li>线程持有的锁</li>
</ol>
</li>
<li><ol start="5">
<li>偏向线程ID</li>
</ol>
</li>
<li><ol start="6">
<li>偏向时间戳</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="2">
<li>类型指针(对象指向他的类的原数据的指针)</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="2">
<li>InstanceData(实例数据)()<ul>
<li><ol>
<li>分配都是按照宽度来分配区域;相同宽度的分配在同一个区域,比如下面这些：</li>
</ol>
</li>
<li><ol start="2">
<li>Longs doubles shorts/chars</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="3">
<li>Padding(堆积填充)<ul>
<li><ol>
<li>这一部分的数据并不是一定就要存在,而是一定的占位符</li>
</ol>
</li>
<li><ol start="2">
<li>对象的字节必须是8个字节的整数倍</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
</ul>
<h5 id="12-深入理解对象的访问定位"><a href="#12-深入理解对象的访问定位" class="headerlink" title="12. 深入理解对象的访问定位"></a>12. 深入理解对象的访问定位</h5><hr>
<h6 id="对象对引用的两种方式：（HotSpot是第二种）"><a href="#对象对引用的两种方式：（HotSpot是第二种）" class="headerlink" title="对象对引用的两种方式：（HotSpot是第二种）"></a>对象对引用的两种方式：（HotSpot是第二种）</h6><ul>
<li><ul>
<li>方式一：使用句柄</li>
</ul>
</li>
<li><ul>
<li>方式二：直接指针</li>
</ul>
</li>
<li>上面指定的是两种方式从 栈内存 中做出一个引用指向到 堆内存中的 一块区域或者是对象本身(广义上讲这样的说法并不是很严谨)即：引用对象<ul>
<li><ol>
<li>如果是指向堆内存中的一块区域的话,这块区域被称为“句柄池”里面保存的就是 对象的地址信息等(这个区域在堆内存中名为句柄池,对象在堆内存中的实例池中)<ul>
<li><ol>
<li>问题：既然可以直接指向对象,为什么还要指向到句柄池再指向到对象?</li>
</ol>
</li>
<li><ol start="2">
<li>答： 栈内存中的地址是不会改变的,在改变的地址是堆内存中的地址,因为堆里面分配的内存可以到处跑或者被GC掉,那么这些会改变的地址都统一显示集中到句柄池里的话,这样栈指向堆的指针就不会野指</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="2">
<li>直接使用指针的话：效率高,减少性能的开销(省去了句柄池这一环节)</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="13-垃圾回收"><a href="#13-垃圾回收" class="headerlink" title="13. 垃圾回收"></a>13. 垃圾回收</h3><hr>
<h5 id="13-1-概述"><a href="#13-1-概述" class="headerlink" title="13.1 概述"></a>13.1 概述</h5><h6 id="1-1-如何判定对象为垃圾对象"><a href="#1-1-如何判定对象为垃圾对象" class="headerlink" title="1.1 如何判定对象为垃圾对象"></a>1.1 如何判定对象为垃圾对象</h6><ul>
<li>引用计数法</li>
<li>可达性分析法<h6 id="1-2-如何回收"><a href="#1-2-如何回收" class="headerlink" title="1.2 如何回收"></a>1.2 如何回收</h6></li>
<li>回收的策略(共4种收集算法)<ul>
<li>标记-清除 算法</li>
<li>复制 算法</li>
<li>标记-整理-清除 算法</li>
<li>分代收集 算法</li>
</ul>
</li>
<li>垃圾回收器<ul>
<li>Serial</li>
<li>Parnew</li>
<li>Cms</li>
<li>G1<h6 id="1-3-何时回收"><a href="#1-3-何时回收" class="headerlink" title="1.3 何时回收"></a>1.3 何时回收</h6></li>
</ul>
</li>
</ul>
<h4 id="①-判断是否为垃圾"><a href="#①-判断是否为垃圾" class="headerlink" title="① 判断是否为垃圾"></a>① 判断是否为垃圾</h4><h5 id="13-2-判断对象是否存活算法-可达性分析法"><a href="#13-2-判断对象是否存活算法-可达性分析法" class="headerlink" title="13.2 判断对象是否存活算法-可达性分析法"></a>13.2 判断对象是否存活算法-可达性分析法</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> 在上面1.1提到如何判定对象对垃圾对象中有个“引用计数法”事实上引用计数法很困难而且不严谨,而且可达性分析法会达到引用计数法达不到的效果<h6 id="2-1-可达性分析思路：GC-Root"><a href="#2-1-可达性分析思路：GC-Root" class="headerlink" title="- 2.1 可达性分析思路：GC Root:"></a>- 2.1 可达性分析思路：GC Root:</h6></li>
<li style="list-style: none"><input type="checkbox" checked> 在栈内存中有个区域里面是GC Root(GC根节点),会去指向(只是一条线或者一根指针)堆内存中的所有区域(这些区域包括new的对象的区域啊等各种各样开辟的内存的区域模块)相当于是串联电路,会将所有的区域串接起来,如果有没有被串接到的区域,那么这个区域就是一个垃圾区域会被等到GC来回收处理(这条 串联导线 称为”引用链”)</li>
</ul>
<h6 id="2-2-作为GC-Roots的对象"><a href="#2-2-作为GC-Roots的对象" class="headerlink" title="- 2.2 作为GC Roots的对象:"></a>- 2.2 作为GC Roots的对象:</h6><ul>
<li><ol>
<li>虚拟机栈</li>
</ol>
</li>
<li><ol start="2">
<li>方法区的类属性所引用的对象</li>
</ol>
</li>
<li><ol start="3">
<li>方法区中常量所引用的对象</li>
</ol>
</li>
<li><ol start="4">
<li>本地方法栈中引用的对象</li>
</ol>
</li>
<li>上面这些都可以作为”GC Root”的节点去向下遍历哪些区域能够被串联起来,哪些不能的就被作为垃圾处理</li>
</ul>
<h4 id="②-回收方法"><a href="#②-回收方法" class="headerlink" title="② 回收方法"></a>② 回收方法</h4><h5 id="13-3-标记-清除算法-——-垃圾回收常用算法之一"><a href="#13-3-标记-清除算法-——-垃圾回收常用算法之一" class="headerlink" title="13.3 标记-清除算法 —— 垃圾回收常用算法之一"></a>13.3 标记-清除算法 —— 垃圾回收常用算法之一</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> “垃圾清除算法” 为 所有垃圾回收算法的基础</li>
</ul>
<h6 id="实施原理："><a href="#实施原理：" class="headerlink" title="实施原理："></a>实施原理：</h6><ul>
<li style="list-style: none"><input type="checkbox" checked> 原理:上面“可达性分析法”里面的GC Root的引用链串接不到的地方(区域等)就会被标记,然后就会被删除(具体怎么标记和怎么删除,没讲清除..)</li>
</ul>
<h6 id="缺点："><a href="#缺点：" class="headerlink" title="缺点："></a>缺点：</h6><ul>
<li>空间问题：<ul>
<li>在堆内存区域中会有无数个区域,并且在整个堆内存中分布较为散乱(比如各种大小和颜色的圈圈),GC将垃圾回收后内存被释放掉(比如同样大小只是没有了颜色的圈圈),那么这个区域依然会在原地而且还是逐个的单个的大小不一的空的区域,并不是一个完整的大的连续的空间;对堆内存空间来说有影响,对再次开辟内存空间来说也有影响</li>
</ul>
</li>
<li>效率问题：<ul>
<li>因为这些区域分布较为散乱(可能一堆有用的区域里的旁边只有一个区域被回收的区域),这个样子当GC回收完成后并不能完全确定是否是真的回收完了,那么此时GC Root还要来遍历一道,GC还要来回收一道;这样就增加了开销降低了效率</li>
</ul>
</li>
</ul>
<h5 id="13-4-复制算法-——-垃圾回收常用算法之二"><a href="#13-4-复制算法-——-垃圾回收常用算法之二" class="headerlink" title="13.4 复制算法 —— 垃圾回收常用算法之二"></a>13.4 复制算法 —— 垃圾回收常用算法之二</h5><h6 id="复制算法-解决了-标记-清楚算法的-两大缺点"><a href="#复制算法-解决了-标记-清楚算法的-两大缺点" class="headerlink" title="复制算法 解决了 标记-清楚算法的 两大缺点"></a>复制算法 解决了 标记-清楚算法的 两大缺点</h6><p>==回顾jvm的内存结构==：从大的方面讲主要有两大块(pic:运行时数据区):</p>
<ul>
<li>线程独享<ul>
<li>虚拟机栈(又分为两个区域)<ul>
<li>新生代(又分为3个区域)<ul>
<li>Eden (伊甸园)(只要是新创建的就会扔到这个地方来,并且GC一直来光顾回收垃圾)</li>
<li>Survivor(存活区,即在Eden里面没有被GC杀掉的)(Survivor是有两块区域)</li>
<li>Tenured Gen(比较稳定,养老级别但是还没退休到老年代)</li>
</ul>
</li>
<li>老年代</li>
</ul>
</li>
<li>本地方法栈</li>
<li>程序计数器</li>
</ul>
</li>
<li>被线程锁共享(又分为两大块)<ul>
<li>堆内存</li>
<li>方法区</li>
</ul>
</li>
</ul>
<h6 id="复制算法原理："><a href="#复制算法原理：" class="headerlink" title="复制算法原理："></a>复制算法原理：</h6><ul>
<li><p>[x] “复制算法”会将内存分为两个区域设为AB区域(AB均属于survivor区域);++第一次++开辟的空间全部都在A空间去开辟,当GC在A空间回收后,“复制算法”会将A空间里面的已经清除了的区域和本来就没有用到的区域复制到B内存区域,那么B内存区域就是一段连续的完整的有效空余区域,A区域已经清理干净;++第二次++来开辟空间的时候在B区域开辟内存区域同样的当GC回收完成后,“复制算法”会将B中的复制到A区域,类似这样循环</p>
</li>
<li><p>这样的话就引入了一个新的问题： </p>
</li>
<li>每次用的时候只是用了内存的一半,造成了内存区域的极大浪费</li>
<li>==那么解决方法：(或新生代内存模型 pic:新生代内存示例)==</li>
<li style="list-style: none"><input type="checkbox" checked> 1. 将内存分为3-4个区域,A区域Eden 80%、B区域Survivor 10%(不稳定存活区域)、C区域Survivor 10%(稳定存活区域)、D区域Tenured Gen 0%(这个区域为新生代里面的老年区域,暂定为0,当C存活区里的对象已经多次存活的话就存入到D老年区,再进行分配);</li>
<li style="list-style: none"><input type="checkbox" checked> 2. 新开辟的空间都在A-Eden(80%)区域中,当GC回收一次垃圾后,将存活的对象放入到B-Survivor(10%)中(B为不稳定存活区域);这样多来几次,B-Survivor(10%)里面会来更多的从Eden里面的存活对象,(GC还偶尔会去B存活区域里面再回收一道)那么在B-Survivor(10%)里面存活的对象已经这几次存活后会存放到C-Survivor(10%)中(C为稳定存活区域),同样的在C区域存活很久的会存放到D(老年区域不是老年代)区域里面;(如果C区域里面的对象装满了,10%不够了,此时就会启动一个内存分配模型,将其他地方的内存通过计算分配点过来(类似于银行贷款,信誉越高贷款越多,如果自己无力还款的话,银行就从担保人那里扣钱,即内存分配模型拿不到内存了就会从另外的区域分配点内存过来)(内存担保-额外的申请内存))</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/jvm/3.jpg" alt="image"></p>
<h6 id="总结：实际解决方法与上面出现的内存浪费并没有太大的关系-只是将内存多分配几个区域或将存活区压缩一下内存而已-上面提到的复制算法的AB区域都是Survivor区域"><a href="#总结：实际解决方法与上面出现的内存浪费并没有太大的关系-只是将内存多分配几个区域或将存活区压缩一下内存而已-上面提到的复制算法的AB区域都是Survivor区域" class="headerlink" title="总结：实际解决方法与上面出现的内存浪费并没有太大的关系,只是将内存多分配几个区域或将存活区压缩一下内存而已;(上面提到的复制算法的AB区域都是Survivor区域)"></a>总结：实际解决方法与上面出现的内存浪费并没有太大的关系,只是将内存多分配几个区域或将存活区压缩一下内存而已;(上面提到的复制算法的AB区域都是Survivor区域)</h6><h5 id="13-5-标记整理算法和分代收集算法——垃圾回收常用算法之三、四"><a href="#13-5-标记整理算法和分代收集算法——垃圾回收常用算法之三、四" class="headerlink" title="13.5 标记整理算法和分代收集算法——垃圾回收常用算法之三、四"></a>13.5 标记整理算法和分代收集算法——垃圾回收常用算法之三、四</h5><h6 id="在-13-4-讲的是回收“新生代”里面的垃圾-此处讲的主要应用于回收“老年代”里面的垃圾-如果新生代里面的垃圾有很难回收的话也可以使用这个方法进行回收"><a href="#在-13-4-讲的是回收“新生代”里面的垃圾-此处讲的主要应用于回收“老年代”里面的垃圾-如果新生代里面的垃圾有很难回收的话也可以使用这个方法进行回收" class="headerlink" title="在 13.4 讲的是回收“新生代”里面的垃圾,此处讲的主要应用于回收“老年代”里面的垃圾(如果新生代里面的垃圾有很难回收的话也可以使用这个方法进行回收)"></a>在 13.4 讲的是回收“新生代”里面的垃圾,此处讲的主要应用于回收“老年代”里面的垃圾(如果新生代里面的垃圾有很难回收的话也可以使用这个方法进行回收)</h6><ul>
<li><p>5.1 标记-整理-收集</p>
</li>
<li><p>[x] 老年代里面垃圾很少,有90%以上的存活,这样GC去清理垃圾的时候效率很低,还会有大量的开销;那么此时会++将“老年代”内存区域分为两个AB两个区域++,①将需要清理的垃圾标记出来,②让稳定存活的到A区域去,需要清理的到B区域去,③GC收集;这样收集的时候GC只是回收了B区域而没有去管A区域就极大的提高了效率</p>
</li>
<li><p>5.2 分代收集算法(结合了标记整理算法和复制算法)<br>“分代收集算法”是根据 新生代 和 老年代 来进行选择垃圾回收算法的;比如：对于垃圾回收较高的采用“复制算法”进行大面积的回收;对于内存回收率较为低的或者是老年代就使用“标记-整理-收集”算法</p>
</li>
</ul>
<h4 id="③-垃圾收集器"><a href="#③-垃圾收集器" class="headerlink" title="③ 垃圾收集器"></a>③ 垃圾收集器</h4><h5 id="13-6-垃圾收集器——serial收集器"><a href="#13-6-垃圾收集器——serial收集器" class="headerlink" title="13.6 垃圾收集器——serial收集器"></a>13.6 垃圾收集器——serial收集器</h5><p>sun公司或者Oracle公司并没有对垃圾回收器做出一个统一的规范,所以每个公司都可以自定义自己的垃圾回收器</p>
<p>为什么垃圾回收器有那么多? —— 答： 根据不同的平台会有不同的垃圾回收器比如PC端移动端等,基本所有的设备都有垃圾回收器,有些可能在服务端和客户端也有不同的垃圾回收器</p>
<h6 id="serial收集器"><a href="#serial收集器" class="headerlink" title="- serial收集器"></a>- serial收集器</h6><ul>
<li>最基本，发展最悠久</li>
<li>单线程垃圾收集器(单开一个线程去回收垃圾)<ul>
<li><ol>
<li>感念：有多个线程在同时运行，此时垃圾回收器要来收集垃圾，就要让这“多个线程”暂停掉，让垃圾回收器的这“一个(单线程)”去执行垃圾回收;这样大大的降低了效率,程序执行一段时间就要停一段时间</li>
</ol>
</li>
<li><ol start="2">
<li>总结：单线程执行有好也有不好;如果是大线程的话那么效率就高;内存比较小的速度比较快的还是效率高比如桌面应用;对于上面讲的这个效率比较低在有些场合还是比较有用的</li>
</ol>
</li>
</ul>
</li>
<li>桌面应用</li>
<li>多用于 新生代内存 多用于 复制算法</li>
</ul>
<h5 id="13-7-垃圾收集器——parnew收集器"><a href="#13-7-垃圾收集器——parnew收集器" class="headerlink" title="13.7 垃圾收集器——parnew收集器"></a>13.7 垃圾收集器——parnew收集器</h5><ul>
<li>复制算法(新生代收集器)</li>
</ul>
<p>上面讲到的“serial收集器”是单个线程收集器;此处的“Parnew收集器”是多个线程的收集器;</p>
<ul>
<li>概念：与上面的“Serial收集器”类似,只是当垃圾回收器去回收的时候执行的线程停了后,垃圾回收器的“多个线程同时”去执行垃圾回收,就上面讲到的单个线程去执行垃圾回收强多了;大大的提升了效率,缩短了时间间隔</li>
<li>但是：在客户端环境下，“Parnew收集器”还是不如serial收集器的效率的</li>
</ul>
<h6 id="CMS"><a href="#CMS" class="headerlink" title="CMS"></a>CMS</h6><ul>
<li style="list-style: none"><input type="checkbox"> JDK1.5 后有个垃圾回收器叫做“CMS”,这款垃圾收集器真正做到了在++方法线程的执行过程中,垃圾回收的线程也能够同步执行++(即边扔垃圾边扫地)</li>
<li style="list-style: none"><input type="checkbox"> 如果CMS用于老年代的垃圾回收的话,那么新生代的垃圾回收必须要使用serial或者parnew这两个中的一个;CMS不能同时工作在新生代和老年代的两个分代空间中</li>
</ul>
<h5 id="13-8-垃圾收集器——parallel-Scavenge-收集器"><a href="#13-8-垃圾收集器——parallel-Scavenge-收集器" class="headerlink" title="13.8 垃圾收集器——parallel Scavenge 收集器"></a>13.8 垃圾收集器——parallel Scavenge 收集器</h5><ul>
<li>复制算法(新生代收集器)</li>
<li>多线程收集器</li>
<li>目标是：可以达到一定的吞吐量<ul>
<li><ol>
<li>吞吐量： CPU用于运行用户代码的时间 与 CPU消耗的总时间的比值</li>
</ol>
</li>
<li><ol start="2">
<li>吞吐量 = (执行用户代码时间) / (执行用户代码的时间 + 垃圾回收所占用的时间)</li>
</ol>
</li>
</ul>
</li>
</ul>
<pre><code>吞吐量 = (执行用户代码时间) / (执行用户代码的时间 + 垃圾回收所占用的时间)
</code></pre><ul>
<li>-XX:MaxGCPauseMillis 垃圾收集器最大停顿时间<ul>
<li>可以用户自定义指定,比如为1,那么就是1ms的时间去回收垃圾(可能并没有将垃圾回收完全,而且回收垃圾的频率就高了,这个需要根据项目具体需要去衡量)</li>
</ul>
</li>
<li>-XX:CGTimeRatio 吞吐量大小<ul>
<li>范围：(1,100)闭区间</li>
<li>比值： 1% - 99%<h6 id="parallel-Scavenge-与-parnew-的区别："><a href="#parallel-Scavenge-与-parnew-的区别：" class="headerlink" title="parallel Scavenge 与 parnew 的区别："></a>parallel Scavenge 与 parnew 的区别：</h6></li>
</ul>
</li>
<li>parallel Scavenge 可以指定行为参数,根据项目具体来设置达到一种优化效果</li>
</ul>
<h5 id="13-9-垃圾收集器-——-CMS-收集器"><a href="#13-9-垃圾收集器-——-CMS-收集器" class="headerlink" title="++13.9 垃圾收集器 —— CMS 收集器++"></a>++13.9 垃圾收集器 —— CMS 收集器++</h5><h6 id="CMS-Concurrent-Mark-Sweep-并发标记清除-收集器"><a href="#CMS-Concurrent-Mark-Sweep-并发标记清除-收集器" class="headerlink" title="CMS:Concurrent Mark Sweep ( 并发标记清除 收集器 )"></a>CMS:Concurrent Mark Sweep ( 并发标记清除 收集器 )</h6><p><img src="https://sxk.s3.didiyunapi.com/pic/jvm/4.jpg" alt="image"></p>
<ul>
<li>工作过程 (与前面的标记-清除算法一致;是老年代垃圾收集器)<ul>
<li>初始标记<ul>
<li><ol>
<li>引用计数法</li>
</ol>
</li>
<li><ol start="2">
<li>可达性分析法 (GCRoot向下遍历)</li>
</ol>
</li>
</ul>
</li>
<li>并发标记<ul>
<li><ol start="3">
<li>接着往下找</li>
</ol>
</li>
</ul>
</li>
<li>重新标记<ul>
<li><ol start="4">
<li>重新标记在初始和并发比较期间因程序运作而产生变动而没有处理好的对象(即对并发标记的修正)</li>
</ol>
</li>
</ul>
</li>
<li>并发清理<ul>
<li><ol start="5">
<li>修正完了过后开始清理垃圾</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
<li>优点<ul>
<li>并发收集</li>
<li>低停顿</li>
</ul>
</li>
<li>缺点<ul>
<li>占用大量的CPU资源(过程很长,还是并发的)</li>
<li>无法处理浮动垃圾(刚刚扫走后又重新扔的垃圾,只有等第二次来扫了)</li>
<li>出现Concurrent Mode Failure(在并发标记和并发清理同时运行的过程中,在并发清理过后重新并发标记的垃圾要扔到一个新的内存空间去,如果这个空间小了就会抛出这个错误如果这个空间大了就会浪费内存)</li>
<li>空间碎片 (标记-清除算法本来就会产生碎片)</li>
</ul>
</li>
</ul>
<p>CMS有这么多的缺点为什么还是要用它? 这里提到两个概念：并行/并发;并行：扫地与清理垃圾同时进行;并发：多个人同时清理垃圾同时仍垃圾;CMS在“标记”和“清理”阶段都非常的耗时间,在这两个阶段都是并行执行的</p>
<h5 id="13-10-最牛的垃圾收集器——-G1-收集器"><a href="#13-10-最牛的垃圾收集器——-G1-收集器" class="headerlink" title="13.10 最牛的垃圾收集器—— G1 收集器"></a>13.10 最牛的垃圾收集器—— G1 收集器</h5><h6 id="Garbage-First"><a href="#Garbage-First" class="headerlink" title="Garbage First"></a>Garbage First</h6><ul>
<li>历史<ul>
<li>2004年sun第一篇G1论文提出</li>
<li>2006年开始计划研制G1</li>
</ul>
</li>
<li>优势<ul>
<li>并行与并发</li>
<li>分代收集(不分新生代还是老年代 会分为无数个Region内存区域)</li>
<li>空间整合</li>
<li>可预测的停顿</li>
</ul>
</li>
<li>步骤<ul>
<li>初始标记(与CMS一致)</li>
<li>并发标记</li>
<li>最终标记(也是并发的)</li>
<li>筛选回收</li>
<li>此处涉及了一个表应用问题比较麻烦“Remember Set”</li>
</ul>
</li>
<li>与CMS相比</li>
</ul>
<h4 id="14-内存分配"><a href="#14-内存分配" class="headerlink" title="14. 内存分配"></a>14. 内存分配</h4><hr>
<h5 id="14-1-概述"><a href="#14-1-概述" class="headerlink" title="14.1 概述"></a>14.1 概述</h5><p>(自动内存管理)</p>
<p>1.1 几大原则：</p>
<ul>
<li>优先分配到Eden</li>
<li>大对象直接分配到老年代</li>
<li>长期存活的对象分配到老年代</li>
<li>空间分配担保(如果新生代的内存不够需要去老年代里面去借)</li>
<li>动态对象的年龄判断</li>
</ul>
<h5 id="14-2-Eden区域"><a href="#14-2-Eden区域" class="headerlink" title="14.2 Eden区域"></a>14.2 Eden区域</h5><h6 id="Eclipse里面打印出垃圾回收相关的信息设置："><a href="#Eclipse里面打印出垃圾回收相关的信息设置：" class="headerlink" title="Eclipse里面打印出垃圾回收相关的信息设置："></a>Eclipse里面打印出垃圾回收相关的信息设置：</h6><ul>
<li style="list-style: none"><input type="checkbox"> 右键运行配置里面“Arguments”中“VM arguments”设置“-verbose:gc -XX:+PrintGCDetails”<ul>
<li>在此输出中会显示当前运行代码的堆内存中的新生代是哪个垃圾收集器收集了什么垃圾,老年代垃圾收集器叫什么名字收集了哪些垃圾</li>
</ul>
</li>
<li style="list-style: none"><input type="checkbox"> 指定哪个收集器来收集垃圾：(同样跟上通用的虚拟机参数)-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC</li>
<li style="list-style: none"><input type="checkbox"> 指定堆内存大小：-Xms20M -Xmx20M</li>
<li style="list-style: none"><input type="checkbox"> 指定新生代内存大小: -Xmn10M (新生代内存设置不好的话会严重影响内存)</li>
<li style="list-style: none"><input type="checkbox"> 新生代内存中Eden区域内存的指定： -XX:SurvivorRatio=8,那么此时两个存活区就各得到一份,Eden就得到8份</li>
<li style="list-style: none"><input type="checkbox" checked> 两种GC的区别：[GC (Allocation Failure)]: 不常用而且耗时是下面的10倍以上;[Full GC (System.gc())]: 常用</li>
</ul>
<h5 id="14-3-大对象直接进入老年代"><a href="#14-3-大对象直接进入老年代" class="headerlink" title="14.3 大对象直接进入老年代"></a>14.3 大对象直接进入老年代</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> -XX:PretenureSizeThreshold</li>
<li><p>上面这个是指定一个数值,内存数值,如果对象的大小大于了这个数值就直接进入到老年代</p>
<pre><code>-verbose:gc -XX:+PrintGCDetails
-XX:+UseSerialGC -Xms20M -Xmn10M
-XX:SuriverRatio=8
-XX:PretenureSizeThreshold=6M
</code></pre></li>
<li><p>JDK8之前数值设为的是字节;JDK8开始可以设为M</p>
</li>
<li>默认值：&lt;=7M放在新生代;&gt;8M放在老年代</li>
</ul>
<h5 id="14-4-长期存活的对象进入老年代"><a href="#14-4-长期存活的对象进入老年代" class="headerlink" title="14.4 长期存活的对象进入老年代"></a>14.4 长期存活的对象进入老年代</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> -XX:MaxTenuringThreshold  15</li>
<li>Age   1 + 1 + 1 </li>
</ul>
<h5 id="14-5-空间分配担保"><a href="#14-5-空间分配担保" class="headerlink" title="14.5 空间分配担保"></a>14.5 空间分配担保</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> -XX:+HandlePromotionFailure</li>
<li>“+”为开启;”-“为禁用</li>
<li>当新生代内存不够的时候 会向老年代内存借;老年代内存会先衡量自身有没有那么多的内存给新生代借去满足新生代的内存需要,如果新生代需要的量 老年代给的出来的话 老年代就会给</li>
<li>当然还要满足其他什么条件,比如什么大于一个平均值啊……</li>
</ul>
<h5 id="14-6-逃逸分析与栈上分配"><a href="#14-6-逃逸分析与栈上分配" class="headerlink" title="14.6 逃逸分析与栈上分配"></a>14.6 逃逸分析与栈上分配</h5><ul>
<li>逃逸分析：分析对象的作用域</li>
</ul>
<h4 id="15-虚拟机工具"><a href="#15-虚拟机工具" class="headerlink" title="15 虚拟机工具"></a>15 虚拟机工具</h4><ul>
<li>Jps</li>
<li>Jstat</li>
<li>Jinfo</li>
<li>Jmap</li>
<li>Jhat</li>
<li>Jstack </li>
<li>jConsole</li>
</ul>
<h5 id="15-1-JPS"><a href="#15-1-JPS" class="headerlink" title="15.1 JPS"></a>15.1 JPS</h5><ul>
<li>JPS-&gt; Java Process status (类似于win的任务管理器)</li>
<li>本地虚拟机唯一id即lvmid: local virtual Machine id<ul>
<li>jps : 查看当前java的进程(即编号,这个编号类似于Linux的PID)</li>
<li>jps -m ：查看当前运行的Java程序的进程</li>
<li>jps -v : 查看jps的版本及jps环境信息</li>
<li>jps -mlv : 更加详细的查看java进程相关信息</li>
</ul>
</li>
<li>m :运行时传入主类的参数</li>
<li>v :虚拟机参数</li>
<li>l :运行的主类全名 或者 jar包名称</li>
</ul>
<h5 id="15-2-Jstat"><a href="#15-2-Jstat" class="headerlink" title="15.2 Jstat"></a>15.2 Jstat</h5><ul>
<li>类装载、内存、垃圾收集、jit编译的信息</li>
<li>用jps查看执行进程编号;在jstat -跟这个编号,就可以查出响应的信息</li>
<li>jstat -gcutil 再跟编号 就会看到一些参数的含义(垃圾回收的摘要信息,对这些信息进行监控);</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/jvm/5.jpg" alt="image"></p>
<ul>
<li>动态监控：</li>
<li>jstat -gcutil 跟jps号 跟多长时间一次监控(ms) 跟监控多少次</li>
<li>eg:         jstat -gcutil 6692 1000 10 //每隔1s的时间监控一次这个进程,共监控10次</li>
<li>==元空间==的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于：元空间并不在虚拟机中,而是使用==本地内存==。因为,默认情况下,元空间的大小仅受本地内存限制</li>
<li>在JDK7包括以前是有永久代的,即“垃圾回收的摘要信息”里面有EOP,现在只有M了,M就是元空间</li>
</ul>
<h5 id="15-3-Jinfo"><a href="#15-3-Jinfo" class="headerlink" title="15.3 Jinfo"></a>15.3 Jinfo</h5><h6 id="实时查看和调整虚拟机的各项参数"><a href="#实时查看和调整虚拟机的各项参数" class="headerlink" title="实时查看和调整虚拟机的各项参数"></a>实时查看和调整虚拟机的各项参数</h6><h6 id="查看：-flag"><a href="#查看：-flag" class="headerlink" title="查看：  -flag "></a>查看：  -flag <name></name></h6><ul>
<li>如果打开了Eclipse,用jps可以看到它的PID进程号,那么jps -v 可以查看响应信息外还可以看到这个进程号使用了哪个垃圾收集器(此处看到的应该是G1即“ -XX:+UseG1GC”)</li>
<li>Jinfo的作用就是可以查看用了哪个虚拟机</li>
<li>Jinfo -flag  UseSerialGC PID   (查看这个PID是否使用了Serial垃圾收集器)<ul>
<li>如果是true:  -XX:+UseSerialGC</li>
<li>如果是false: -XX:-UseSerialGC</li>
</ul>
</li>
<li>Jinfo -flag UseG1GC pid (与上类似)<ul>
<li>“+”,“-” 表示是启用和不启用</li>
</ul>
</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/jvm/6.jpg" alt="image"></p>
<h6 id="调整：-flag-启动或者禁用某个VM"><a href="#调整：-flag-启动或者禁用某个VM" class="headerlink" title="调整：- flag [+/-]  //启动或者禁用某个VM"></a>调整：- flag [+/-] <name> //启动或者禁用某个VM</name></h6><h6 id="调整：-flag-设置VM赋予一个什么值"><a href="#调整：-flag-设置VM赋予一个什么值" class="headerlink" title="调整：-flag  =  //设置VM赋予一个什么值"></a>调整：-flag <name> = <value> //设置VM赋予一个什么值</value></name></h6><h6 id="查看：-flags-打印虚拟机的参数"><a href="#查看：-flags-打印虚拟机的参数" class="headerlink" title="查看：-flags  打印虚拟机的参数"></a>查看：-flags  打印虚拟机的参数</h6><h6 id="查看：-sysprops-打印Java系统的配置变量"><a href="#查看：-sysprops-打印Java系统的配置变量" class="headerlink" title="查看：-sysprops 打印Java系统的配置变量"></a>查看：-sysprops 打印Java系统的配置变量</h6><h5 id="15-4-Jmap"><a href="#15-4-Jmap" class="headerlink" title="15.4 Jmap"></a>15.4 Jmap</h5><ul>
<li>Jmap 与 Jinfo 的用法差不多一致;</li>
<li><p>另外这些工具里面的命令不是每个命令都能使用;有些命令在Linux下才能使用会根据系统平台而定</p>
<pre><code>jmap -dump:format=b,file=c:\a.bin PID
</code></pre></li>
<li><p>上面这个命令是生成这个PID(Java进程)的快照;再用分析工具来分析下这个“a.bin”文件的内存啊堆栈啊垃圾回收啊之类的信息(导出堆栈信息)</p>
</li>
<li><p>之前是用的在Eclipse里面设置jvm参数来导出错误信息</p>
<pre><code>-XX:+HeapDumpOnOutOfMemoryErroe
</code></pre></li>
<li><p>jmap -histo PID  //是查看所有存活的什么的状态</p>
<ul>
<li>里面会看到 类的信息 和 实例的信息</li>
<li>如果查看到的信息很多的话,可以使用类似于Linux中的管道命令(下面这个是分页查询,通过管道命令将很多信息进行分页)<ul>
<li>eg： jmap -histo 8286 | more </li>
</ul>
</li>
</ul>
</li>
</ul>
<h5 id="15-5-Jhat"><a href="#15-5-Jhat" class="headerlink" title="15.5 Jhat"></a>15.5 Jhat</h5><h6 id="Jhat-JVM-heap-Analysis-Tool"><a href="#Jhat-JVM-heap-Analysis-Tool" class="headerlink" title="Jhat: JVM heap Analysis Tool"></a>Jhat: JVM heap Analysis Tool</h6>
          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/03/12/redis/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/03/12/redis/" itemprop="url">redis</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-03-12T02:17:59+08:00">
                2018-03-12
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <blockquote>
<p>@Author:<a href="mailto:songxiankun2008@126.com" target="_blank" rel="noopener">songxiankun2008@126.com</a></p>
</blockquote>
<h3 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a>Redis</h3><hr>
<h6 id="EHcache"><a href="#EHcache" class="headerlink" title="EHcache"></a>EHcache</h6><ul>
<li>轻量级缓存 </li>
<li>他是运行在单机内存里的 方便简单</li>
<li>缺点：容量非常有限 应用场合主要是单机应用 单机范围内<h6 id="Mem-cache"><a href="#Mem-cache" class="headerlink" title="Mem cache"></a>Mem cache</h6></li>
<li>内容存储很单一 就是字符串<h6 id="redis"><a href="#redis" class="headerlink" title="redis"></a>redis</h6></li>
<li>存储类型丰富 性能非常高 可靠性很高</li>
<li>Redis不仅仅支持简单的k/v类型的数据，同时还提供list，set，zset，hash等数据结构的存储。memcache支持简单的数据类型，String。</li>
<li>Redis支持数据的备份，即master-slave模式的数据备份。</li>
<li>Redis支持数据的持久化，可以将内存中的数据保持在磁盘中，重启的时候可以再次加载进行使用。<h6 id="Memcache-amp-redis"><a href="#Memcache-amp-redis" class="headerlink" title="Memcache &amp; redis"></a>Memcache &amp; redis</h6></li>
<li><ol>
<li>由于Redis只使用单核，而Memcached可以使用多核，所以在比较上，平均每一个核上 Redis在存储小数据时Memcached性能更高。而在100k以上的数据中，Memcached性能要高于Redis，虽然Redis最近也在存储 大数据的性能上进行优化，但是比起Memcached，还是稍有逊色。说了这么多，结论是，无论你使用哪一个，每秒处理请求的次数都不会成为瓶颈。</li>
</ol>
</li>
<li><ol start="2">
<li>如果要说内存使用效率，使用简单的key-value存储的话，Memcached的内存利用率更高，而如果Redis采用hash结构来做key-value存储，由于其合组式的压缩，其内存利用率会高于Memcached。</li>
</ol>
</li>
<li><ol start="3">
<li>如果你对数据持久化和数据同步有所要求，那么推荐你选择Redis，因为这两个特性Memcached都不具备。即使你只是希望在升级或者重启系统后缓存数据不会丢失，选择Redis也是明智的。</li>
</ol>
</li>
<li>Redis和Memcache都是将数据存放在内存中，都是内存数据库。不过memcache还可用于缓存其他东西，例如图片、视频等等。</li>
<li>虚拟内存–Redis当物理内存用完时，可以将一些很久没用到的value 交换到磁盘</li>
<li>过期策略–memcache在set时就指定，例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定，例如expire name 10</li>
<li>分布式–设定memcache集群，利用magent做一主多从;redis可以做一主多从。都可以一主一从</li>
<li>存储数据安全–memcache挂掉后，数据没了；redis可以定期保存到磁盘（持久化）</li>
<li>灾难恢复–memcache挂掉后，数据不可恢复; redis数据丢失后可以通过aof恢复</li>
<li>Memcached是多线程，非阻塞IO复用的网络模型；Redis使用单线程的IO复用模型。</li>
</ul>
<p><img src="https://user-gold-cdn.xitu.io/2018/4/18/162d7773080d4570?imageslim" alt="image"></p>
<h5 id="1-redis在Linux上的安装"><a href="#1-redis在Linux上的安装" class="headerlink" title="1. redis在Linux上的安装"></a>1. redis在Linux上的安装</h5><ol>
<li>安装redis编译的c环境，yum install gcc-c++</li>
<li>将redis-2.6.16.tar.gz上传到Linux系统中</li>
<li>解压到/usr/local下  tar -xvf redis-2.6.16.tar.gz -C /usr/local</li>
<li>进入redis-2.6.16目录 使用make命令编译redis</li>
<li>在redis-2.6.16目录中 使用make PREFIX=/usr/local/redis install命令安装            redis到/usr/local/redis中</li>
<li>拷贝redis-2.6.16中的redis.conf到安装目录redis中</li>
<li>启动redis 在bin下执行命令redis-server redis.conf</li>
<li>如需远程连接redis，需配置redis端口6379在linux防火墙中开发<br>/sbin/iptables -I INPUT -p tcp –dport 6379 -j ACCEPT<br>/etc/rc.d/init.d/iptables save</li>
<li>将redis.conf文件中的daemonize从false修改成true表示后台启动</li>
<li>若要远程连接的话,1.<code>bind 127.0.0.1改为 #bind 127.0.0.1</code>,2.<code>protected-mode yes 改为 protected-mode no</code><h5 id="2-Redis的常用命令"><a href="#2-Redis的常用命令" class="headerlink" title="2. Redis的常用命令"></a>2. Redis的常用命令</h5></li>
</ol>
<h6 id="redis是一种高级的key-value的存储系统"><a href="#redis是一种高级的key-value的存储系统" class="headerlink" title="redis是一种高级的key-value的存储系统"></a>redis是一种高级的key-value的存储系统</h6><p>其中的key是字符串类型，尽可能满足如下几点：</p>
<ol>
<li>key不要太长，最好不要操作1024个字节，这不仅会消耗内存还会降低查找        效率</li>
<li>key不要太短，如果太短会降低key的可读性</li>
<li>在项目中，key最好有一个统一的命名规范（根据企业的需求）</li>
<li>其中value    支持五种数据类型：<ul>
<li>字符串型 string</li>
<li>字符串列表 lists</li>
<li>字符串集合 sets</li>
<li>有序字符串集合 sorted sets</li>
<li>哈希类型 hashs</li>
</ul>
</li>
</ol>
<h5 id="3-存储字符串string"><a href="#3-存储字符串string" class="headerlink" title="3. 存储字符串string"></a>3. 存储字符串string</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> 字符串类型是Redis中最为基础的数据存储类型，它在Redis中是二进制安全的，这便意味着该类型可以接受任何格式的数据，如JPEG图像数据或Json对象描述信息等。    在Redis中字符串类型的Value最多可以容纳的数据长度是512M</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/string.png" alt="image"></p>
<ul>
<li>1）set key value：设定key持有指定的字符串value，如果该key存在则进行覆盖    操作。总是返回”OK”</li>
<li>2）get key：获取key的value。如果与该key关联的value不是String类型，redis    将返回错误信息，因为get命令只能用于获取String value；如果该key不存在，返    回null</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/1.png" alt="image"></p>
<ul>
<li>3）getset key value：先获取该key的值，然后在设置该key的值。</li>
<li>4）incr key：将指定的key的value原子性的递增1.如果该key不存在，其初始值    为0，在incr之后其值为1。如果value的值不能转成整型，如hello，该操作将执    行失败并返回相应的错误信息。</li>
<li>5）decr key：将指定的key的value原子性的递减1.如果该key不存在，其初始值    为0，在incr之后其值为-1。如果value的值不能转成整型，如hello，该操作将执    行失败并返回相应的错误信息。</li>
<li>6）incrby key increment：将指定的key的value原子性增加increment，如果该    key不存在，器初始值为0，在incrby之后，该值为increment。如果该值不能转成    整型，如hello则失败并返回错误信息</li>
<li>7）decrby key decrement：将指定的key的value原子性减少decrement，如果    该key不存在，器初始值为0，在decrby之后，该值为decrement。如果该值不能    转成整型，如hello则失败并返回错误信息</li>
<li>8）append key value：如果该key存在，则在原有的value后追加该值；如果该    key    不存在，则重新创建一个key/value</li>
</ul>
<h5 id="4-存储lists类型"><a href="#4-存储lists类型" class="headerlink" title="4. 存储lists类型"></a>4. 存储lists类型</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> 在Redis中，List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表    一样，我们可以在其头部(left)和尾部(right)添加新的元素。在插入时，如果该键并不存在，Redis将为该键创建一个新的链表。与此相反，如果链表中所有的元素均被移    除，那么该键也将会被从数据库中删除。</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/2.png" alt="image"></p>
<ul>
<li>1）==lpush key value1 value2…==：在指定的key所关联的list的头部插入所有的    values，如果该key不存在，该命令在插入的之前创建一个与该key关联的空链    表，之后再向该链表的头部插入数据。插入成功，返回元素的个数。</li>
<li>2）==rpush key value1、value2…==：在该list的尾部添加元素</li>
<li>3）lrange key start end：获取链表中从start到end的元素的值，start、end可    为负数，若为-1则表示链表尾部的元素，-2则表示倒数第二个，依次类推…</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/3.png" alt="image"></p>
<ul>
<li>4）lpushx key value：仅当参数中指定的key存在时（如果与key管理的list中没    有值时，则该key是不存在的）在指定的key所关联的list的头部插入value。</li>
<li>5）rpushx key value：在该list的尾部添加元素</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/4.png" alt="image"></p>
<ul>
<li>6）lpop key：返回并弹出指定的key关联的链表中的第一个元素，即头部元素。</li>
<li><p>7）rpop key：从尾部弹出元素</p>
</li>
<li><p>8）rpoplpush resource destination：将链表中的尾部元素弹出并添加到头部</p>
</li>
<li>9）llen key：返回指定的key关联的链表中的元素的数量。</li>
<li>10）lset key index value：设置链表中的index的脚标的元素值，0代表链表的头元    素，-1代表链表的尾元素。</li>
<li>11）lrem key count value：删除count个值为value的元素，如果count大于0，从头向尾遍历并删除count个值为value的元素，如果count小于0，则从尾向头遍历并删除。如果count等于0，则删除链表中所有等于value的元素。</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/5.png" alt="image"></p>
<ul>
<li>12）linsert key before|after pivot value：在pivot元素前或者后插入value这个元素</li>
</ul>
<h5 id="5-存储sets类型"><a href="#5-存储sets类型" class="headerlink" title="5. 存储sets类型"></a>5. 存储sets类型</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> 在Redis中，我们可以将Set类型看作为没有排序的字符集合，和List类型一样，我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是，这些操作的时间是常量时间。Set可包含的最大元素数是4294967295。</li>
<li><p>[x] ==和List类型不同的是，Set集合中不允许出现重复的元素==。和List类型相比，Set类    型在功能上还存在着一个非常重要的特性，即在服务器端完成多个Sets之间的聚合计    算操作，如unions、intersections和differences。由于这些操作均在服务端完成，    因此效率极高，而且也节省了大量的网络IO开销</p>
</li>
<li><p>1）==sadd key value1、value2…==：向set中添加数据，如果该key的值已有则不会      重复添加</p>
</li>
<li>2）==smembers key==：获取set中所有的成员</li>
<li>3）scard key：获取set中成员的数量</li>
<li>4）sismember key member：判断参数中指定的成员是否在该set中，1表示存      在，0表示不存在或者该key本身就不存在</li>
<li>5）srem key member1、member2…：删除set中指定的成员</li>
<li>6）srandmember key：随机返回set中的一个成员</li>
<li>7）sdiff sdiff key1 key2：返回key1与key2中相差的成员，而且与key的顺序有      关。即返回差集。</li>
<li>8）sdiffstore destination key1 key2：将key1、key2相差的成员存储在              destination上</li>
<li>9）sinter key[key1,key2…]：返回交集。</li>
<li>10）sinterstore destination key1 key2：将返回的交集存储在destination上</li>
<li>11）sunion key1、key2：返回并集。</li>
<li>12）sunionstore destination key1 key2：将返回的并集存储在destination上</li>
</ul>
<h5 id="6-存储sortedset"><a href="#6-存储sortedset" class="headerlink" title="6. 存储sortedset"></a>6. 存储sortedset</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> Sorted-Sets和Sets类型极为相似，它们都是字符串的集合，==都不允许重复的成员出现在一个Set中==。它们之间的主要差别是Sorted-Sets中的每一个成员都会有一个分数(score)与之关联，Redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是，尽管Sorted-Sets中的成员必须是唯一的，但是分数(score)    却是可以重复的。<br>在Sorted-Set中添加、删除或更新一个成员都是非常快速的操作，其时间复杂度为    集合中成员数量的对数。由于Sorted-Sets中的成员在集合中的位置是有序的，因此，    即便是访问位于集合中部的成员也仍然是非常高效的。事实上，Redis所具有的这一    特征在很多其它类型的数据库中是很难实现的，换句话说，在该点上要想达到和Redis    同样的高效，在其它数据库中进行建模是非常困难的。</li>
<li><p>[ ] 例如：游戏排名、微博热点话题等使用场景。</p>
</li>
<li><p>1）==zadd key score member score2 member2 …== ：将所有成员以及该成员的        分数存放到sorted-set中</p>
</li>
<li>2）zcard key：获取集合中的成员数量</li>
<li>3）zcount key min max：获取分数在[min,max]之间的成员<ul>
<li>zincrby key increment member：设置指定成员的增加的分数。</li>
<li>zrange key start end [withscores]：获取集合中脚标为start-end的成员，[withscores]参数表明返回的成员包含其分数。</li>
<li>zrangebyscore key min max [withscores] [limit offset count]：返回分数在[min,max]的成员并按照分数从低到高排序。[withscores]：显示分数；[limit offset count]：offset，表明从脚标为offset的元素开始并返回count个成员。</li>
<li>zrank key member：返回成员在集合中的位置。</li>
<li>zrem key member[member…]：移除集合中指定的成员，可以指定多个成员。</li>
<li>zscore key member：返回指定成员的分数</li>
</ul>
</li>
</ul>
<h5 id="7-存储hash"><a href="#7-存储hash" class="headerlink" title="7. 存储hash"></a>7. 存储hash</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> Redis中的Hashes类型可以看成具有String Key和String Value的map容器。所以该类型==非常适合于存储值对象的信息==。如Username、Password和Age等。如果    Hash中包含很少的字段，那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash    可以存储4294967295个键值对。</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/6.png" alt="image"></p>
<ul>
<li>1）==hset key field value==:为指定的key设定field/value对（键值对）。</li>
<li>2）==hgetall key==：获取key中的所有filed-vaule</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/7.png" alt="image"></p>
<ul>
<li>3）==hget key field==：返回指定的key中的field的值</li>
<li>4）hmset key fields：设置key中的多个filed/value</li>
<li>5）hmget key fileds：获取key中的多个filed的值</li>
<li>6）hexists key field：判断指定的key中的filed是否存在</li>
<li>7）hlen key：获取key所包含的field的数量</li>
<li>8）hincrby key field increment：设置key中filed的值增加increment，如：age        增加20</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/11.png" alt="image"></p>
<hr>
<h4 id="8-数据类型String-分布式锁"><a href="#8-数据类型String-分布式锁" class="headerlink" title="8.数据类型String 分布式锁"></a>8.数据类型String 分布式锁</h4><ul>
<li>设置过期时间:==expire== key time 单位s</li>
<li><p>分布式锁：==setnx==  key value</p>
<ul>
<li>当key不存在时就新建这个key(只有这个key不存在的时候才会成功)</li>
</ul>
</li>
<li><p>set   设置/添加</p>
</li>
<li>get     获取</li>
<li>incr    自增</li>
<li>decr    自减</li>
<li>incrby  自定义增加</li>
<li>decrby  自定义减少</li>
<li>del key 删除</li>
</ul>
<h4 id="9-数据类型-Hash-List-Set-SortedSet-排行榜"><a href="#9-数据类型-Hash-List-Set-SortedSet-排行榜" class="headerlink" title="9. 数据类型 Hash List Set SortedSet 排行榜"></a>9. 数据类型 Hash List Set SortedSet 排行榜</h4><p><em>Hash</em> &lt;String,values&gt;</p>
<ul>
<li>hmset  key field value   //存储Hash<ul>
<li>eg: hmset laowang name “laowang” age “28” look “handsome”</li>
</ul>
</li>
<li><p>hgetall  key       //获取全部</p>
<ul>
<li>eg: hgetall laowang //此时会将上面存储的全部get出来</li>
</ul>
</li>
<li><p>hget key field  //获取Hash中的单个</p>
<ul>
<li>eg: hget laowang age   //输出28</li>
<li>hset laowang age “88”  //设 值 - 此处即覆盖    </li>
</ul>
</li>
</ul>
<p><em>List</em> &lt;单列集合&gt;</p>
<ul>
<li>lpush  可以当做集合也可以当做堆栈<ul>
<li>eg: lpush laowang aaa bbb ccc </li>
<li>再lpush一下,是数值叠加,不是覆盖</li>
</ul>
</li>
<li>lrange key start stop // 范围获取,从哪个值到哪个值的范围之中<ul>
<li>lrange laowang 0 2 //表示取出前面三个元素</li>
<li>输出为：ccc bbb aaa  //因为是堆栈,先进后出 后进先出</li>
<li>lrange laowang 0 100 //只要尾数大于容量数就可以获取全部的值</li>
</ul>
</li>
<li><p>lindex key index    // 获取专门哪个值,索引脚标对应的值</p>
<ul>
<li>lindex laowang 1   //输出bbb </li>
</ul>
</li>
<li><p>lpop key    //表示将这个key中的栈顶的值弹出(即get到后删除)</p>
<ul>
<li>eg: lpop laowang  //会输出”ccc” 再获取下全部就没有了”ccc”</li>
</ul>
</li>
<li>rpop key  // 表示将 栈底 的那个值给弹出 (r:reverse 倒着)<ul>
<li>eg: rpop laowang  //会输出”aaa”</li>
</ul>
</li>
<li>lset key index value// 将index值修改,即覆盖<ul>
<li>eg: lset laowang 0  xxx //原理laowang下的首个元素值被改成了”xxx”</li>
</ul>
</li>
<li>llen key //查看key的长度<ul>
<li>eg: llen laowang</li>
</ul>
</li>
<li>lrem key count value  //删除哪些元素<ul>
<li>eg: count是指从顶自下几个元素删除   </li>
</ul>
</li>
</ul>
<p><strong>set  &lt;不能有重复&gt;</strong></p>
<h6 id="没有索引"><a href="#没有索引" class="headerlink" title="没有索引"></a>没有索引</h6><ul>
<li>sadd key value1 value2 value3…  //往这个key里面存值<ul>
<li>eg: sadd  laowang aaa bbb ccc  //返回”3” 表示成功操作了3个数据  </li>
</ul>
</li>
<li>smembers key //获取这个key下面的所有成员值(Members)<ul>
<li>eg:smembers laowang   (取出来没得重复的元素)</li>
</ul>
</li>
<li><p>若此时再添加aaa:  sadd laowang aaa 则会返回”0”表示没有数据操作,再获取所有：smembers laowang 输出的是 aaa,bbb,ccc;并不会出现两个aaa</p>
</li>
<li><p>spop key [count] //[]表示这个值可有可无;如果没有则表示默认移除栈顶元素;若有指定的值,就从顶向下数到count值进行移除</p>
<ul>
<li>eg: spop laowang  //移除 栈顶元素</li>
<li>eg: spop laowang 2 //移除上面两个元素</li>
</ul>
</li>
<li>srem key member [member …]  //删除key下那个元素(名称)<ul>
<li>eg: srem laowang aaa bbb //将laowang中的aaa,bbb给删除</li>
</ul>
</li>
<li>sinter key [key …] // 取出两个key之中 一样的情况</li>
<li>sdiff key [key …] // 取出两个key之中 不一样的情况<ul>
<li>sinter laowang laowang2   取出这两个集合的相同点;</li>
<li>sdiff  laowang laowang2   对这两个集合进行求差值</li>
</ul>
</li>
</ul>
<p><strong>SortedSet &lt;有序的set&gt;</strong></p>
<ul>
<li>zadd laowang 3 ccc 2 aaa 5 fff<ul>
<li>存入(根据sort进行排序)</li>
</ul>
</li>
<li>zrange key start stop [withScores] //取出,可选伴随分数<ul>
<li>zrange laowang 0 5 取出</li>
<li>输出后是,分数较小的在上面;即：”aaa” “ccc” “fff”</li>
</ul>
</li>
<li>zrange laowang 0 10 withscores <ul>
<li>输出是： “aaa” “2” “ccc” “3” “fff” “5”</li>
</ul>
</li>
<li>此时若插入一个相同的值,不同的分数;eg:zadd laowang 99 aaa;此时会返回”0”表示没有数据执行成功,即没有第二个aaa;但是原先那个aaa的分数由原来的2变成了现在的99;那么在获取全部输出的时候aaa将会排在最后输出(值按照分数从小到大排列)</li>
</ul>
<p>普通代码：</p>
<pre><code>public class Main {
    public static void main(String[] args) {
        Jedis jedis = new Jedis(&quot;127.0.0.1&quot;, 6379, 60);
        jedis.select(9);
        jedis.set(&quot;java&quot;, &quot;123&quot;);
        jedis.setnx(&quot;java&quot;, &quot;789&quot;);
        jedis.set(&quot;c&quot;, &quot;c++&quot;);
        System.out.println(jedis.get(&quot;java&quot;);
        System.out.println(jedis.mget(&quot;java&quot;,&quot;c&quot;));
        Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;();
        map.put(&quot;one&quot;, &quot;first&quot;);
        map.put(&quot;one&quot;, &quot;2&quot;);
        map.put(&quot;two&quot;, &quot;WW&quot;);
        map.put(&quot;3&quot;, &quot;Three&quot;);
        jedis.hmset(&quot;LaLa&quot;, map);
        System.out.println(jedis.hmget(&quot;LaLa&quot;,&quot;one&quot;));
        System.out.println(jedis.hgetAll(&quot;LaLa&quot;));
    }
}
</code></pre><p><img src="https://sxk.s3.didiyunapi.com/pic/redis/8.jpg" alt="image"></p>
<h4 id="10-Redis发布订阅"><a href="#10-Redis发布订阅" class="headerlink" title="10. Redis发布订阅"></a>10. Redis发布订阅</h4><h6 id="Redis发布订阅的模型"><a href="#Redis发布订阅的模型" class="headerlink" title="Redis发布订阅的模型"></a>Redis发布订阅的模型</h6><p><img src="https://sxk.s3.didiyunapi.com/pic/redis/9.jpg" alt="image"></p>
<ul>
<li>订阅频道：所有的redis客户端执行命令<code>subscribe</code>跟频道名称;<ul>
<li>eg: <code>subscribe laowang</code></li>
</ul>
</li>
<li>发布消息：一个没有订阅这个ChannelId的客户端去推送消息发布<ul>
<li>publish ChannelId Message</li>
<li>publish laowang  Shot-Him</li>
</ul>
</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/10.png" alt="image"></p>
<h6 id="用代码实现消息订阅与发布"><a href="#用代码实现消息订阅与发布" class="headerlink" title="用代码实现消息订阅与发布:"></a>用代码实现消息订阅与发布:</h6><p>①消息订阅的实现：</p>
<pre><code>public class RedisSubscribe extends JedisPubSub {
    public RedisSubscribe() {
        super();
    }

    @Override
    public void onMessage(String channel, String message) {
        System.out.println(channel + &quot;   &quot; + message);
    }
</code></pre><p>②消息的订阅(监听)：</p>
<pre><code>public class subscribe {
    public static void main(String[] args) {
        Jedis jedis = new Jedis(&quot;127.0.0.1&quot;, 6379, 60);
        //    subscribe 订阅/订购
        jedis.subscribe(new RedisSubscribe(),&quot;laowang&quot;);
    }
}
    //消息订阅 是一个阻塞线程;一直都处于等待状态,有消息就才处理
</code></pre><p>③消息的发布：   ( 上下的②跟③是在两个项目中 )</p>
<pre><code>public class publish {
    public static void main(String[] args) {
        Jedis jedis = new Jedis(&quot;127.0.0.1&quot;, 6379, 60);
        jedis.publish(&quot;laowang&quot;, &quot;这里是要发布的消息!&quot;);
    }
}
</code></pre><ul>
<li style="list-style: none"><input type="checkbox" checked> redis的发布订阅是 异步、解耦到其他的系统里面去;独立起来各与各之间没得关联;一处有问题不会影响其他地方的执行;</li>
</ul>
<h4 id="11-redis事务"><a href="#11-redis事务" class="headerlink" title="11. redis事务"></a>11. redis事务</h4><ul>
<li><p>multi    //开启事务(执行的第一行代码)  </p>
</li>
<li><p>set key values //装值 (执行的第二行代码)</p>
<ul>
<li>eg: set aaa aaaa 会返回”QUEUED”表示进入队列成功</li>
<li>可以再继续 set bb bbb ;同样会返回”QUEUED”(执行的第三行代码)</li>
</ul>
</li>
<li>get key //通过键获取值 (执行的第四行代码)<ul>
<li>eg: get aaa</li>
</ul>
</li>
<li>exec //执行 (第五行代码即最后一行——执行！)</li>
</ul>
<h6 id="第一行代码是事务开始-中间3行代码是执行数据的代码-最后一行是启动“执行”这一效果来按顺序执行上面的3行数据代码-此时返回的值是OK-OK-“aaaa”"><a href="#第一行代码是事务开始-中间3行代码是执行数据的代码-最后一行是启动“执行”这一效果来按顺序执行上面的3行数据代码-此时返回的值是OK-OK-“aaaa”" class="headerlink" title="第一行代码是事务开始,中间3行代码是执行数据的代码,最后一行是启动“执行”这一效果来按顺序执行上面的3行数据代码;此时返回的值是OK OK  “aaaa”"></a>第一行代码是事务开始,中间3行代码是执行数据的代码,最后一行是启动“执行”这一效果来按顺序执行上面的3行数据代码;此时返回的值是OK OK  “aaaa”</h6><ul>
<li><p>如果上面的exec执行失败;会有两种报错:</p>
<ul>
<li><ol>
<li>事务中途出错(或语法出错),exec后不会操作数据,如果刚刚有添加数据,那么此时获取这个数据是获取不到的;只会回滚 </li>
</ol>
</li>
<li><ol start="2">
<li>事务过程中没有出错,在exec执行的时候才出错;是里面插入的数据类型不符合但是redis没有报错没有检测出来在exec的时候才检测出来,那么在这个应该检测出来的这一行命令之前的命令已经执行成功了;虽然此时exec是执行失败了但是前面已经执行成功了的数据依然是执行成功了的并不会数据回滚;这样的话这些脏数据只能自己手动清理</li>
</ol>
</li>
</ul>
</li>
<li><p>watch key [keys …] // 监控  </p>
</li>
</ul>
<h6 id="如果是在multi事务中要涉及到上面监控的这个key的值-如果要修改这个key的值那么exec执行后-会显示执行无效-因为这个-key-是在被监控的状态是不能被修改的-除非是执行了-unwatch-取消监控的命令后再重复这一套mutil-gt-exec事务才能执行成功"><a href="#如果是在multi事务中要涉及到上面监控的这个key的值-如果要修改这个key的值那么exec执行后-会显示执行无效-因为这个-key-是在被监控的状态是不能被修改的-除非是执行了-unwatch-取消监控的命令后再重复这一套mutil-gt-exec事务才能执行成功" class="headerlink" title="如果是在multi事务中要涉及到上面监控的这个key的值,如果要修改这个key的值那么exec执行后,会显示执行无效; 因为这个 key 是在被监控的状态是不能被修改的;除非是执行了 unwatch 取消监控的命令后再重复这一套mutil-&gt;exec事务才能执行成功"></a>如果是在multi事务中要涉及到上面监控的这个key的值,如果要修改这个key的值那么exec执行后,会显示执行无效; 因为这个 key 是在被监控的状态是不能被修改的;除非是执行了 unwatch 取消监控的命令后再重复这一套mutil-&gt;exec事务才能执行成功</h6><h6 id="相当于是加了锁-出了watch这个事务外-在其他事务或者操作中要操作我这儿的东西可能会被拒绝或者一直等待"><a href="#相当于是加了锁-出了watch这个事务外-在其他事务或者操作中要操作我这儿的东西可能会被拒绝或者一直等待" class="headerlink" title="相当于是加了锁;(出了watch这个事务外,在其他事务或者操作中要操作我这儿的东西可能会被拒绝或者一直等待)"></a>相当于是加了锁;(出了watch这个事务外,在其他事务或者操作中要操作我这儿的东西可能会被拒绝或者一直等待)</h6><p>unwatch  取消监控</p>
<h4 id="12-redis持久化"><a href="#12-redis持久化" class="headerlink" title="12. redis持久化"></a>12. redis持久化</h4><ul>
<li>1.快照(一般不用)<ul>
<li>redis的快照是默认配置</li>
<li>优点: 能够随便保存,数据保存的比较全</li>
<li>缺点：随便保存如果数据量很大的话磁盘大小会受不了,并且读写磁盘的效率比较低,大大的浪费了效率;只是900s改变了一个key然而把好多G的数据全部down在了磁盘造成了数据的浪费</li>
</ul>
</li>
</ul>
<h6 id="redis持久化策略-默认快照-快照的默认配置"><a href="#redis持久化策略-默认快照-快照的默认配置" class="headerlink" title="redis持久化策略(默认快照,快照的默认配置)"></a>redis持久化策略(默认快照,快照的默认配置)</h6><p>•    1. RDB：基于特定的时间间隔将数据 “全量快照”，生成 RDB 文件并落地</p>
<pre><code>SNAPSHOTTING (快照)

# Save the DB on disk:
#
#   save &lt;seconds&gt; &lt;changes&gt;

save 900 1   //如果900秒内发生了一次key的改变就生成一次快照
save 300 10 //如果300秒内发生了10次改变就生成一次快照
save 60 10000 //如果60s内...

//  1 跟 2 的配置文件是 redis.windows.conf
</code></pre><ul>
<li>2.aop (append only file)(公司多用这个)<ul>
<li>仅添加被改变的;将被改变的添加到某持久化的磁盘中</li>
</ul>
</li>
</ul>
<h6 id="redis持久化策略-Append-only-mode"><a href="#redis持久化策略-Append-only-mode" class="headerlink" title="redis持久化策略(Append only mode)"></a>redis持久化策略(Append only mode)</h6><p>此模式默认状态下是关闭的;如果要开启aop这个持久化策略的话,需要将上面的3个“save”全部用“#”关闭;</p>
<pre><code>再将
appendonly no
置为
appendonly yes
</code></pre><p>策略：</p>
<pre><code># appendfsync always   //有改变就持久化到磁盘
appendfsync everysec   //每秒钟同步一次(非常建议用 好用)
# appendfsync no      //完全依赖于操作系统或者redis,不能认为控制
</code></pre><h5 id="RDB-与-AOF-优缺点和选择"><a href="#RDB-与-AOF-优缺点和选择" class="headerlink" title="RDB 与 AOF 优缺点和选择"></a>RDB 与 AOF 优缺点和选择</h5><h6 id="RDB"><a href="#RDB" class="headerlink" title="RDB"></a>RDB</h6><ul>
<li>非常适合于备份以及灾难恢复的场景</li>
<li>能够最大化 Redis 性能</li>
<li>相对于 AOF，RDB 文件在 Redis 启动时能够更快加载</li>
<li>若期望将数据丢失的可能性最小化，RDB 并不适用<h6 id="AOF"><a href="#AOF" class="headerlink" title="AOF"></a>AOF</h6></li>
<li>基于 “追加” 和 “文件同步” 的特性，AOF 具有更佳的 “持久化” 表现</li>
<li><p>对于相同的数据，AOF 文件大小通常将超过 RDB</p>
</li>
<li><p>[x] 综合而言，如果能够承担一定程度的数据丢失风险，仅启用 RDB 持久化即可。但并不建议只启用 AOF 持久化，毕竟 RDB 文件更适合于数据备份。</p>
</li>
<li style="list-style: none"><input type="checkbox" checked> 若 RDB 持久化和 AOF 持久化同时启用，Redis 启动时，将加载 AOF 文件，毕竟 AOF 具有更佳的 “持久化” 表现。<h5 id="redis淘汰算法"><a href="#redis淘汰算法" class="headerlink" title="==redis淘汰算法=="></a>==redis淘汰算法==</h5></li>
<li>redis可以持久化,所以内存容量会越来越小</li>
<li><p>于是就有了淘汰算法</p>
<pre><code># maxmemory &lt;bytes&gt;  //最大能存多大
# maxmemory-policy noeviction //有了上面那个就必有这个(最大内存策略);即超过了这个最大内存会怎么办
</code></pre></li>
<li><p>此时就引入了一个新的算法：LRU</p>
<h6 id="LRU-最近使用算法-很长时间不同的-进行淘汰-从redis删除"><a href="#LRU-最近使用算法-很长时间不同的-进行淘汰-从redis删除" class="headerlink" title="LRU: 最近使用算法 很长时间不同的 进行淘汰 从redis删除"></a>LRU: 最近使用算法 很长时间不同的 进行淘汰 从redis删除</h6></li>
<li><p>redis中lru的策略机制：(每个key的使用都会有一个时间戳,越历史的时间的越容易被删除)</p>
</li>
<li>①:</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># maxmemory-policy noeviction </span><br><span class="line">//noeviction</span><br><span class="line">表示没有设置过期的key虽然永久不过期但是还是要用,所以不要把这个key给删除</span><br></pre></td></tr></table></figure>
<ul>
<li><p>②:      </p>
<pre><code># maxmemory-policy volatile-lru
//volatile-lru表示将会删除永久不过期的key
</code></pre></li>
<li><p>其他：</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">    # volatile-lru -&gt; remove the key with an expire set using an LRU algorithm</span><br><span class="line">    # allkeys-lru -&gt; remove any key according to the LRU algorithm</span><br><span class="line">    # volatile-random -&gt; remove a random key with an expire set</span><br><span class="line">    # allkeys-random -&gt; remove a random key, any key</span><br><span class="line">    # volatile-ttl -&gt; remove the key with the nearest expire time (minor TTL)</span><br><span class="line">    # noeviction -&gt; don&apos;t expire at all, just return an error on write operations</span><br><span class="line">    </span><br><span class="line">1. volatile-lru：从已设置过期时间的数据集（server.db[i].expires）中挑选最近最少使用的数据淘汰</span><br><span class="line"></span><br><span class="line">2. volatile-ttl：从已设置过期时间的数据集（server.db[i].expires）中挑选将要过期的数据淘汰</span><br><span class="line"></span><br><span class="line">3. volatile-random：从已设置过期时间的数据集（server.db[i].expires）中任意选择数据淘汰</span><br><span class="line"></span><br><span class="line">4. allkeys-lru：从数据集（server.db[i].dict）中挑选最近最少使用的数据淘汰</span><br><span class="line"></span><br><span class="line">5. allkeys-random：从数据集（server.db[i].dict）中任意选择数据淘汰</span><br><span class="line"></span><br><span class="line">6. no-enviction（驱逐）：禁止驱逐数据</span><br></pre></td></tr></table></figure>
<h4 id="13-redis-集群-…"><a href="#13-redis-集群-…" class="headerlink" title="13. redis 集群 …"></a>13. redis 集群 …</h4><ul>
<li>redis 3.0 之后才能支持集群;</li>
<li>在redis3.0出来之前要想搭集群是有一套“哨兵”机制:</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/12.jpg" alt="image"></p>
<h6 id="redis2-0"><a href="#redis2-0" class="headerlink" title="redis2.0 +"></a>redis2.0 +</h6><ul>
<li>redis的“哨兵”机制与Nginx的“KeepAlive”心跳存活检测是相似的道理;检测到哪一个挂了就用另一个顶替上去</li>
<li>“Sentinel”即哨兵系统是一套主从分离系统;从图中可以看到是有一个主要的server1在工作,下面的server234都是在复制server1的数据</li>
<li>当这个主服务器Server1挂掉后,server1就立刻下线,让server2来当这个Master服务器</li>
<li>缺点：Master服务器与Slave服务器一直都在保持一致的数据,导致数据重复数据浪费数据占据了大量的内存空间<h6 id="redis3-0"><a href="#redis3-0" class="headerlink" title="redis3.0 +"></a>redis3.0 +</h6></li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/13.jpg" alt="image"></p>
<ul>
<li>redis的集群一般都是6台(6/8/10)三主三从</li>
<li>安装Ruby环境</li>
<li>新建6个文件夹,分别在6个目录中新建6个配置文件例redis.6379.conf</li>
<li>配置内容：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">不配置了,这就是一个鸡肋..现在没得用的</span><br></pre></td></tr></table></figure>
<h4 id="Redis-“主-从”-机制"><a href="#Redis-“主-从”-机制" class="headerlink" title="Redis “主 - 从” 机制"></a>Redis “主 - 从” 机制</h4><hr>
<p>Redis 提供 “主 - 从” 的数据复制：“从” Redis 即作为 “主” Redis 的数据副本。“从” Redis，既能够用于读性能的扩展，亦能够作为数据备份的一种手段。</p>
<p>同时，Redis 支持 Redis Sentinel，实现 “主 - 从” 监控、故障迁移，限于篇幅，本文不予以展开。</p>
<p><strong>工作机制</strong></p>
<h6 id="“主-从”-数据复制的基本工作机制"><a href="#“主-从”-数据复制的基本工作机制" class="headerlink" title="“主 - 从” 数据复制的基本工作机制"></a>“主 - 从” 数据复制的基本工作机制</h6><ul>
<li>已建立的 “主” - “从” 连接，“主” Redis 不断地将命令发送到 “从” Redis</li>
<li>若连接中断（例如：网络问题），“从” Redis 将尝试重新建立连接，并尝试 “半 - 重新同步”</li>
<li>若无法进行 “半 - 重新同步”，“从” Redis 将尝试进行 “重新同步”（“主 - 从” 连接首次建立，亦执行 “重新同步”）<br>关于 “半 - 重新同步” &amp; “重新同步”</li>
<li>“半 - 重新同步”：“从” Redis 将尝试获取连接中断期间于 “主” Redis 执行的命令（存储于 backlog）</li>
<li>“重新同步”<ul>
<li>“主” Redis 创建数据快照（RDB 文件）、同步到 “从” Redis，开始将 “主” Redis 执行的命令发送到 “从” Redis</li>
<li>“从” Redis 丢弃当前数据，加载 “主” Redis 的 RDB 文件，开始执行 “主” Redis 发送的命令</li>
</ul>
</li>
</ul>
<p>“数据复制” 对于 “主” Redis 全部是异步的；对于 “从” Redis，大部分是异步的，但 “重新同步” 涉及 “丢弃当前数据，加载 RDB 文件”，将引起 “短暂中断”。</p>
<p><strong>“主 - 从” 配置</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">slaveof master_ip master_port  </span><br><span class="line"># “从” Redis 配置：“主” Redis - IP &amp;  port</span><br><span class="line"></span><br><span class="line">masterauth master_password     # “从” Redis 配置：“主” Redis - 密码</span><br><span class="line"></span><br><span class="line">slave-serve-stale-data yes     </span><br><span class="line"># “从” Redis 配置：当 “主 - 从” 连接中断或 “从” Redis 正在进行初始化同步，“从” Redis 是否提供服务：</span><br><span class="line">#   yes: 默认，以 “从” Redis 当前数据提供服务</span><br><span class="line">#   no: 对于接收到的命令，“从” Redis 返回 “SYNC in progress”（INFO、SLAVEOF命令除外）</span><br><span class="line"></span><br><span class="line">slave-read-only yes            </span><br><span class="line"># “从” Redis 配置：是否 “只读”，默认 yes</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">“主” Redis 配置：根据已连接的 “从” Redis 情况，“主” Redis 是否接收 “写命令”</span><br><span class="line">min-slaves-to-write 3</span><br><span class="line">min-slaves-max-lag 10</span><br><span class="line">表示：最少有 3 个已连接的 “从” Redis，且延迟小于等于 10 秒</span><br><span class="line"></span><br><span class="line">min-slaves-to-write 3          # 默认 0，即无论 “从” Redis 的连接情况，始终接收 “写命令”</span><br><span class="line">min-slaves-max-lag 10</span><br></pre></td></tr></table></figure>
<p>以上的代码，仅列出了部分关键的配置。其他类似于：diskless 复制、backlog 配置，限于篇幅，未能列出，详情内容请参考 redis.conf for Redis 2.8。</p>
<p><strong>“主 - 从” 命令</strong></p>
<h6 id="1-SLAVEOF-host-port"><a href="#1-SLAVEOF-host-port" class="headerlink" title="1. SLAVEOF host port"></a>1. SLAVEOF host port</h6><p>将 Redis 配置作为 “从” Redis，其 “主” Redis 位置即为 host:port。</p>
<h6 id="2-SLAVEOF-NO-ONE"><a href="#2-SLAVEOF-NO-ONE" class="headerlink" title="2. SLAVEOF NO ONE"></a>2. SLAVEOF NO ONE</h6><p>终止 “从” Redis 自 “主” Redis 的数据同步。<br>特别说明：SLAVEOF NO ONE 包含了 Redis<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">设计之初，关于 “自由” 的思想：“If slavery is not wrong, nothing is wrong. -- Abraham Lincoln”。</span><br></pre></td></tr></table></figure></p>
<p><strong>“主 - 从” 链</strong></p>
<p>“从” Redis 能够作为其他 Redis的“主”Redis，由此构建级联结构的“主 - 从”链。并且，“主” Redis能够与多个“从”Redis建立连接，建立“树状”结构。</p>
<p><img src="https://s3.didiyunapi.com/sxk/pic/redis/17.png" alt="image"></p>
<p>图中所示，对于扩展读性能，非常有益。</p>
<h4 id="优化-Redis-内存使用"><a href="#优化-Redis-内存使用" class="headerlink" title="优化 Redis 内存使用"></a>优化 Redis 内存使用</h4><p>合理的 Redis 实例，内存的占有量不应当超过 60%，当内存使用率过高时，应该予以清理及优化。</p>
<h5 id="使用-ziplist-amp-intset"><a href="#使用-ziplist-amp-intset" class="headerlink" title="使用 ziplist &amp; intset"></a>使用 ziplist &amp; intset</h5><ul>
<li>ziplist 优化机制</li>
<li>ziplist 实现了 “紧凑” 的数据结构，通过尽可能减少非数据节点的占用，以提供内存密度。</li>
</ul>
<p><img src="https://s3.didiyunapi.com/sxk/pic/redis/18.png" alt="image"></p>
<h6 id="图中所示，ziplist-整体结构："><a href="#图中所示，ziplist-整体结构：" class="headerlink" title="图中所示，ziplist 整体结构："></a>图中所示，ziplist 整体结构：</h6><p>•    zl-bytes：整个 ziplist 占用内存的字节数</p>
<p>•    zl-tail：ziplist 尾节点距离起始地址的字节数</p>
<p>•    zl-len：ziplist 包含的节点数量</p>
<p>•    entry：节点</p>
<p>•    zl-end：ziplist 末端标记，固定 0xFF<br>ziplist 节点结构：</p>
<p>•    previous-entry-length：前一个节点占用内存的字节数</p>
<p>•    encoding：节点编码，明确节点存储内容属于 “字节数组” 或整数，并明确长度（即占用的字节数）</p>
<p>•    content：节点存储内容</p>
<p>“散列表”、“链表”、“有序集合”，使用 ziplist，受益于其 “紧凑” 的数据结构，相较于 hashtable、linkedlist、skiplist，能够有效减少内存占用。</p>
<p>然而，受限于 “紧凑” 的数据结构，随着节点数量增长和节点大小膨胀，基于 ziplist 实现的 “散列表”、“链表”、“有序集合”，性能将显著下降。</p>
<p><strong>intset 优化机制</strong></p>
<p>intset 使用整型数组作为存储的数据结构。通常，hashtable 实现的 Redis 集合，其成员以 “字符串” 结构进行存储，intset 由此能够显著降低内存使用。</p>
<p>类似于 ziplist，同样受限于其整型数组，“集合” 成员数量的增长将引起 “集合” 性能的下降。<br><strong>涉及配置</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">“散列表” 使用 ziplist 的限制条件：</span><br><span class="line">  - 成员数量不超过 hash-max-ziplist-entries</span><br><span class="line">  - 最大内存占用的成员，内存占用不超过 hash-max-ziplist-value (字节)</span><br><span class="line">  两者必须同时具备，任意条件不满足，即无法使用 ziplist</span><br><span class="line"></span><br><span class="line">hash-max-ziplist-entries 512</span><br><span class="line">hash-max-ziplist-value 64</span><br><span class="line"></span><br><span class="line">“链表” 使用 ziplist 的限制条件</span><br><span class="line"></span><br><span class="line">list-max-ziplist-entries 512</span><br><span class="line">list-max-ziplist-value 64</span><br><span class="line"></span><br><span class="line">“有序集合” 使用 ziplist 的限制条件</span><br><span class="line"></span><br><span class="line">zset-max-ziplist-entries 128</span><br><span class="line">zset-max-ziplist-value 64</span><br><span class="line"></span><br><span class="line">“集合” 使用 intset 的限制条件:</span><br><span class="line">- 成员全部为 64 位有符号整数</span><br><span class="line">- 成员数量不超过 set-max-intset-entries</span><br><span class="line"></span><br><span class="line">set-max-intset-entries 512</span><br></pre></td></tr></table></figure>
<p>附加说明：ziplist &amp; intset 的限制条件，是基于内存占用和性能的综合考虑。</p>
<p><strong>数据分片</strong></p>
<h6 id="分布式-“数据分片”"><a href="#分布式-“数据分片”" class="headerlink" title="分布式 “数据分片”"></a>分布式 “数据分片”</h6><p>分布式 “数据分片”：选取合适的方式将 Redis 数据分布于不同的实例，由此降低单实例的内存使用，实现优化。请参考 “扩展 Redis 的容量和性能” 章节。</p>
<h6 id="单实例-“数据分片”"><a href="#单实例-“数据分片”" class="headerlink" title="单实例 “数据分片”"></a>单实例 “数据分片”</h6><p>通常而言，单实例 “数据分片”，并不能直接降低 Redis 内存使用，需要结合 ziplist 等内存优化方式，以 “散列表” 为例：</p>
<p>•    以散列表的键作为 “数据分片” 的 “路由”，将单个内存占用量大的 “散列表” 分片到多个内存占有量小的 “散列表”</p>
<p>•    内存占有量小的 “散列表”（例如：“散列表” 成员数量少） 能够以 ziplist 方式减少内存占用</p>
<p>由此，有效地实现内存使用的优化。</p>
<p><strong>基于业务进行优化</strong></p>
<p>基于业务，通常能够取得良好的 Redis 内存优化效果，例如：</p>
<p>•    尽可能短的 Redis 键，例如：以 “u_178” 替代 “user_id_178”</p>
<p>•    选择合适的 Redis 数据结构，例如：合理地选择 “散列表” 替代 “字符串”，若 “字符串” 数量较少，使用一个 “散列表” 替代，通常能够减少内存使用</p>
<p>•    减少存储于 Redis 的业务数据量</p>
<h4 id="扩展-Redis-的容量和性能"><a href="#扩展-Redis-的容量和性能" class="headerlink" title="扩展 Redis 的容量和性能"></a>扩展 Redis 的容量和性能</h4><p>Redis 作为任何单实例的数据服务，最终会遇到容量和性能瓶颈。前文阐述的 Redis “主 - 从”，即为常见且有效的扩展 Redis 读性能的方案。</p>
<p>本章节，主要关注基于 “数据分片” 构建 Redis 集群。常用的方案包括：Redis Cluster、twemproxy、Codis。</p>
<h5 id="“数据分片”-使用-Codis"><a href="#“数据分片”-使用-Codis" class="headerlink" title="“数据分片” - 使用 Codis"></a>“数据分片” - 使用 Codis</h5><h6 id="选择-Codis-的原因"><a href="#选择-Codis-的原因" class="headerlink" title="选择 Codis 的原因"></a>选择 Codis 的原因</h6><p>Codis 来自于 “豌豆荚”，相对于 twemproxy，选择 Codis 的原因：</p>
<p>•    twemproxy 无法实现动态水平扩展</p>
<p>•    Codis 运行于多核机器能够获得更好的应用</p>
<p>相对于 Redis Cluster，选择 Codis 的原因：</p>
<p>•    Redis Cluster 必须使用 Redis 3.0 以上版本的客户端</p>
<p>•    Redis Cluster 无法支持 pipeline</p>
<h5 id="Codis-架构"><a href="#Codis-架构" class="headerlink" title="Codis 架构"></a>Codis 架构</h5><p><img src="https://s3.didiyunapi.com/sxk/pic/redis/19.png" alt="image"></p>
<p>图中所示，Codis 架构中引入了 codis-proxy，由 codis-proxy 基于 Redis key 计算分片，将命令转发到 codis-group，因此：对于绝大多数的命令，客户端对于 Codis 的接入是透明的。</p>
<p>Codis 针对 Redis key 计算 CRC32，默认分为 1024 个 Slot，进而路由到特定的 codis-group，实现分片。</p>
<p>除了 “数据分片”，Codis 的特性还包括：</p>
<p>•    提供了 codis-fe &amp; codis-dashborad 作为集群管理工具</p>
<p>•    允许多个 codis-proxy，实现 proxy 层的高可用</p>
<p>•    codis-group 支持 “主 - 从”，引入 redis-sentinel 实现 “主 - 从” 故障迁移</p>
<p>必须说明的是：“数据分片” 扩展容量和性能的同时，亦限制了 Redis 若干方便的能力，例如：Codis 不支持事务、部分命令不支持。</p>
<h4 id="基于-Lua-的-Redis-“存储过程”"><a href="#基于-Lua-的-Redis-“存储过程”" class="headerlink" title="基于 Lua 的 Redis “存储过程”"></a>基于 Lua 的 Redis “存储过程”</h4><p>Redis 内置了 Lua 解释器（Redis 2.6.0 起），允许使用 Lua（版本 5.1）进行服务器端脚本编程，即类似于 “存储过程” 相似。</p>
<h5 id="Lua-服务器脚本编程涉及的-Redis-命令"><a href="#Lua-服务器脚本编程涉及的-Redis-命令" class="headerlink" title="Lua 服务器脚本编程涉及的 Redis 命令"></a>Lua 服务器脚本编程涉及的 Redis 命令</h5><h6 id="1-EVAL-script-numkeys-key-key-…-arg-arg-…"><a href="#1-EVAL-script-numkeys-key-key-…-arg-arg-…" class="headerlink" title="1. EVAL script numkeys key [key …] arg [arg …]"></a>1. EVAL script numkeys key [key …] arg [arg …]</h6><p>于 Redis 服务端上下文：</p>
<p>•    执行 script 脚本</p>
<p>•    命令参数 numkeys key [key …] 即为 key 集合（numkeys 即为集合大小）</p>
<p>•    命令参数 arg [arg …] 即为传递到脚本的附加参数集合。</p>
<p>特别说明：建议将脚本可能会读取或写入的 key 全部通过 EVAL 命令的 numkeys key [key …] 参数传递（于 Redis 集群非常有益）。</p>
<h6 id="2-SCRIPT-LOAD-script"><a href="#2-SCRIPT-LOAD-script" class="headerlink" title="2. SCRIPT LOAD script"></a>2. SCRIPT LOAD script</h6><p>将 script 脚本载入到 Redis 服务端，并返回脚本的 SHA1 摘要。</p>
<h6 id="3-EVALSHA-sha1-numkeys-key-key-…-arg-arg-…"><a href="#3-EVALSHA-sha1-numkeys-key-key-…-arg-arg-…" class="headerlink" title="3. EVALSHA sha1 numkeys key [key …] arg [arg …]"></a>3. EVALSHA sha1 numkeys key [key …] arg [arg …]</h6><p>与 EVAL 命令相似，通过 SCRIPT LOAD 命令获得的 sha1 明确需要执行的脚本。</p>
<h5 id="Lua-与-Redis-交互"><a href="#Lua-与-Redis-交互" class="headerlink" title="Lua 与 Redis 交互"></a>Lua 与 Redis 交互</h5><h6 id="Lua-脚本获取-EVAL-amp-EVALSHA-命令的参数"><a href="#Lua-脚本获取-EVAL-amp-EVALSHA-命令的参数" class="headerlink" title="Lua 脚本获取 EVAL &amp; EVALSHA 命令的参数"></a>Lua 脚本获取 EVAL &amp; EVALSHA 命令的参数</h6><p>通过 Lua 脚本的全局变量 KEYS 和 ARGV，能够访问 EVAL 和 EVALSHA 命令的 key [key …] 参数和 arg [arg …] 参数。</p>
<p>作为 Lua Table，能够将 KEYS 和 ARGV 作为一维数组使用，其下标从 1 开始。</p>
<h6 id="Lua-脚本内部执行-Redis-命令"><a href="#Lua-脚本内部执行-Redis-命令" class="headerlink" title="Lua 脚本内部执行 Redis 命令"></a>Lua 脚本内部执行 Redis 命令</h6><p>Lua 脚本内部允许通过内置函数执行 Redis 命令：</p>
<p>•    redis.call()</p>
<p>•    redis.pcall()</p>
<p>函数的第 1 个参数即为 Redis 命令，命令的参数即为函数的其他参数。</p>
<p>两者非常相似，区别在于：若 Redis 命令执行错误，redis.call() 将错误抛出（即 EVAL &amp; EVALSHA 执行出错）；redis.pcall() 将错误内容返回。</p>
<h5 id="Lua-与-Redis-数据类型"><a href="#Lua-与-Redis-数据类型" class="headerlink" title="Lua 与 Redis 数据类型"></a>Lua 与 Redis 数据类型</h5><p>Redis 与 Lua 脚本的交互，必然涉及 Lua 和 Redis 的数据类型转换。限于篇幅，本文仅列出若干关键点。完整的数据类型转换请参阅 redis.io。</p>
<h5 id="1-Lua-提供-boolean-数据类型，Redis-无-boolean-数据类型"><a href="#1-Lua-提供-boolean-数据类型，Redis-无-boolean-数据类型" class="headerlink" title="1. Lua 提供 boolean 数据类型，Redis 无 boolean 数据类型"></a>1. Lua 提供 boolean 数据类型，Redis 无 boolean 数据类型</h5><p>•    “true” (Lua) 转换为 “整数 1” (Redis)</p>
<p>•    “false” (Lua) 与 “nil” (Redis) 互相转换</p>
<h5 id="2-Lua-仅提供单一的数值类型-number，不区分浮点数和整数"><a href="#2-Lua-仅提供单一的数值类型-number，不区分浮点数和整数" class="headerlink" title="2. Lua 仅提供单一的数值类型 number，不区分浮点数和整数"></a>2. Lua 仅提供单一的数值类型 number，不区分浮点数和整数</h5><p>•    number (Lua) 转换为 “整数”（Redis），舍弃 number 的小数部分</p>
<p>•    若需要传递 “浮点数”，必须使用字符串</p>
<h5 id="3-Lua-Table-与-Redis-“multi-bulk-reply”-相互转换"><a href="#3-Lua-Table-与-Redis-“multi-bulk-reply”-相互转换" class="headerlink" title="3. Lua Table 与 Redis “multi bulk reply” 相互转换"></a>3. Lua Table 与 Redis “multi bulk reply” 相互转换</h5><p>若 Table (Lua) 的某一成员被转换为 nil (Redis)，Table (Lua) 后续成员的转换即终止</p>
<h6 id="示例：-“接口调用延时”-统计分析的组件"><a href="#示例：-“接口调用延时”-统计分析的组件" class="headerlink" title="示例： “接口调用延时” 统计分析的组件"></a>示例： “接口调用延时” 统计分析的组件</h6><p>使用 Lua，除了能够简化代码、减少与 Redis 的交互，一个重要的收益：Lua 脚本、单个 Redis 命令、“MULTI / EXEC” 事务，都是原子操作。</p>
<p>《如何基于 Redis 构建应用程序组件》 阐述了 “接口调用延时” 统计分析的组件。本文章使用 Lua 脚本编程，重新实现 “接口调用延时” “上报” 接口。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">获取 Redis 连接</span><br><span class="line"></span><br><span class="line">def get_connection():</span><br><span class="line">return redis.Redis.from_url(&apos;redis://127.0.0.1:6379/&apos;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">“接口调用延时” 数据上报 - Lua 脚本初始化</span><br><span class="line"></span><br><span class="line">def init_report(conn = None):</span><br><span class="line">  if conn is None:</span><br><span class="line">    conn = get_connection()</span><br><span class="line"></span><br><span class="line">  return conn.script_load(&apos;&apos;&apos;</span><br><span class="line">  local time_delay = ARGV[1]</span><br><span class="line"></span><br><span class="line">  local temp_key_for_max = KEYS[1]</span><br><span class="line">  local temp_key_for_min = KEYS[2]</span><br><span class="line">  local statistics_key_for_max = KEYS[3]</span><br><span class="line">  local statistics_key_for_min = KEYS[4]</span><br><span class="line">  local statistics_key_for_sum_count = KEYS[5]</span><br><span class="line"></span><br><span class="line">  redis.call(&apos;zadd&apos;, temp_key_for_max, time_delay, &apos;max&apos;)</span><br><span class="line">  redis.call(&apos;zunionstore&apos;, statistics_key_for_max, 2, statistics_key_for_max, temp_key_for_max, &apos;AGGREGATE&apos;, &apos;MAX&apos;)</span><br><span class="line"></span><br><span class="line">  redis.call(&apos;zadd&apos;, temp_key_for_min, time_delay, &apos;min&apos;)</span><br><span class="line">  redis.call(&apos;zunionstore&apos;, statistics_key_for_min, 2, statistics_key_for_min, temp_key_for_min, &apos;AGGREGATE&apos;, &apos;MIN&apos;)</span><br><span class="line"></span><br><span class="line">  redis.call(&apos;zincrby&apos;, statistics_key_for_sum_count, 1, &apos;count&apos;)</span><br><span class="line">  redis.call(&apos;zincrby&apos;, statistics_key_for_sum_count, time_delay, &apos;sum&apos;)</span><br><span class="line"></span><br><span class="line">  redis.call(&apos;del&apos;, temp_key_for_max, temp_key_for_min)</span><br><span class="line">  &apos;&apos;&apos;)</span><br></pre></td></tr></table></figure>
<p>“接口调用延时” 数据上报</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">def report(interface, time_delay, conn = None, function = None):</span><br><span class="line">  if conn is None:</span><br><span class="line">    conn = get_connection()</span><br><span class="line"></span><br><span class="line">  if function is None:</span><br><span class="line">    function = init_report(conn)</span><br><span class="line"></span><br><span class="line">  temp_key_for_max = str(uuid.uuid4())</span><br><span class="line">  temp_key_for_min = str(uuid.uuid4())</span><br><span class="line"></span><br><span class="line">  raw_key = &apos;statistics_%s_%d&apos; % (interface, (int(time.time()) / 60))</span><br><span class="line">  statistics_key_for_max = &apos;%s_for_max&apos; % (raw_key, )</span><br><span class="line">  statistics_key_for_min = &apos;%s_for_min&apos; % (raw_key, )</span><br><span class="line">  statistics_key_for_sum_count = &apos;%s_for_sum_count&apos; % (raw_key, )</span><br><span class="line"></span><br><span class="line">  conn.evalsha(function, 5, temp_key_for_max, temp_key_for_min, statistics_key_for_max, statistics_key_for_min, statistics_key_for_sum_count, time_delay)</span><br></pre></td></tr></table></figure>
<p>基于 Redis 特性，Lua 脚本执行过程中，Redis 不能接收其他命令，假如执行时间过长（超过 Redis 配置项 lua-time-limit），此时：</p>
<p>•    若 Lua 脚本没有进行 “写” 操作，能够通过 SCRIPT KILL 安全地终止 Lua 脚本；</p>
<p>•    否则，为了确保 Lua 脚本的 “原子性” ，仅允许通过 SHUTDOWN NOSAVE 强制终止 Redis 进程</p>
<hr>
<h5 id="Redis-Cluster分区实现原理"><a href="#Redis-Cluster分区实现原理" class="headerlink" title="Redis Cluster分区实现原理"></a>Redis Cluster分区实现原理</h5><ul>
<li>槽(solt)概念</li>
<li style="list-style: none"><input type="checkbox" checked> Redis Cluster中有一个16384长度的槽的概念,他们的编号为0、1 、2…..16382 、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster中每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务,至于哪个Master节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rn脚本)。这里值得一提的是,在 Redis Cluster 中,只有Master才拥有槽的所有权,如果是某个Master的slave,这个slave只负责槽的使用,但是没有所有权。redis Cluster怎么知道哪些槽是由哪些节点负责的呢？某个Master又怎么知道某个槽自己是不是拥有呢?</li>
<li>位序列结构</li>
<li style="list-style: none"><input type="checkbox" checked> Master节点维护着一个16384/8字节的位序列,Master节点用bit来标识对于某个槽自己是否拥有。比如对于编号为1的槽,Master只要判断序列的第二位(索引从0开始)是不是为1即可。如果在当下找不到key 返回给客户端</li>
<li>一致性哈希<ul>
<li>之前的哈希算法是 HashCode%6=一个数;这个数就是一个key值,6表示有6台集群机器;如果某一台机器宕机了就是 HashCode%5=另一个数;这两个数完全不一样即Key值不一样;导致在各个片区上面存储的东西也有了变化;后来就衍生出了“一致性哈希”</li>
</ul>
</li>
</ul>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/14.jpg" alt="image"></p>
<h6 id="一致性哈希-将所有的key值映射到一个圆环上面去-对圆环分成几个片区-在这每一个片区上面又分布几个key值-这样key并没有完全分布在同一个地方-而且-如果其中的某一个片区-机器-挂掉后-这个片区上面的key值并不会消失而是将这些key值就近分配到左右周围片区上面去-与其他片区上面的key值融合在一起"><a href="#一致性哈希-将所有的key值映射到一个圆环上面去-对圆环分成几个片区-在这每一个片区上面又分布几个key值-这样key并没有完全分布在同一个地方-而且-如果其中的某一个片区-机器-挂掉后-这个片区上面的key值并不会消失而是将这些key值就近分配到左右周围片区上面去-与其他片区上面的key值融合在一起" class="headerlink" title="一致性哈希:将所有的key值映射到一个圆环上面去;对圆环分成几个片区,在这每一个片区上面又分布几个key值;这样key并没有完全分布在同一个地方,而且,如果其中的某一个片区(机器)挂掉后,这个片区上面的key值并不会消失而是将这些key值就近分配到左右周围片区上面去,与其他片区上面的key值融合在一起;"></a>一致性哈希:将所有的key值映射到一个圆环上面去;对圆环分成几个片区,在这每一个片区上面又分布几个key值;这样key并没有完全分布在同一个地方,而且,如果其中的某一个片区(机器)挂掉后,这个片区上面的key值并不会消失而是将这些key值就近分配到左右周围片区上面去,与其他片区上面的key值融合在一起;</h6><p><img src="https://sxk.s3.didiyunapi.com/pic/redis/15.jpg" alt="image"></p>
<p>redis 是单进程 单线程</p>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/16.jpg" alt="image"></p>
<h6 id="Redis的并发竞争问题如何解决"><a href="#Redis的并发竞争问题如何解决" class="headerlink" title="Redis的并发竞争问题如何解决?"></a>Redis的并发竞争问题如何解决?</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Redis为单进程单线程模式，采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念，Redis对于多个客户端连接并不存在竞争，但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题，这些问题均是由于客户端连接混乱造成。对此有2种解决方法：</span><br><span class="line"></span><br><span class="line">1.客户端角度，为保证每个客户端间正常有序与Redis进行通信，对连接进行池化，同时对客户端读写Redis操作采用内部锁synchronized。</span><br><span class="line"></span><br><span class="line">2.服务器角度，利用setnx实现锁。</span><br><span class="line">注：对于第一种，需要应用程序自己处理资源的同步，可以使用的方法比较通俗，可以使用synchronized也可以使用lock；第二种需要用到Redis的setnx命令，但是需要注意一些问题。</span><br></pre></td></tr></table></figure>
<h6 id="Redis-大量数据插入"><a href="#Redis-大量数据插入" class="headerlink" title="Redis 大量数据插入"></a>Redis 大量数据插入</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">有些时候，Redis实例需要装载大量用户在短时间内产生的数据，数以百万计的keys需要被快速的创建，我们称之为大量数据插入(mass insertion)</span><br><span class="line"></span><br><span class="line">方案：使用Luke协议</span><br></pre></td></tr></table></figure>
<h4 id="缓存世界中的三大问题及解决方案"><a href="#缓存世界中的三大问题及解决方案" class="headerlink" title="++缓存世界中的三大问题及解决方案++"></a><em>++缓存世界中的三大问题及解决方案++</em></h4><h5 id="1-缓存穿透"><a href="#1-缓存穿透" class="headerlink" title="1. 缓存穿透"></a>1. 缓存穿透</h5><h6 id="1-当业务系统发起某一个查询请求时，首先判断缓存中是否有该数据；"><a href="#1-当业务系统发起某一个查询请求时，首先判断缓存中是否有该数据；" class="headerlink" title="1.    当业务系统发起某一个查询请求时，首先判断缓存中是否有该数据；"></a>1.    当业务系统发起某一个查询请求时，首先判断缓存中是否有该数据；</h6><h6 id="2-如果缓存中存在，则直接返回数据；"><a href="#2-如果缓存中存在，则直接返回数据；" class="headerlink" title="2.    如果缓存中存在，则直接返回数据；"></a>2.    如果缓存中存在，则直接返回数据；</h6><h6 id="3-如果缓存中不存在，则再查询数据库，然后返回数据。"><a href="#3-如果缓存中不存在，则再查询数据库，然后返回数据。" class="headerlink" title="3.    如果缓存中不存在，则再查询数据库，然后返回数据。"></a>3.    如果缓存中不存在，则再查询数据库，然后返回数据。</h6><h5 id="1-1-什么是缓存穿透？"><a href="#1-1-什么是缓存穿透？" class="headerlink" title="1.1 什么是缓存穿透？"></a>1.1 什么是缓存穿透？</h5><p>业务系统要查询的数据根本就存在！当业务系统发起查询时，按照上述流程，首先会前往缓存中查询，由于缓存中不存在，然后再前往数据库中查询。由于该数据压根就不存在，因此数据库也返回空。这就是缓存穿透。</p>
<p>综上所述：业务系统访问压根就不存在的数据，就称为缓存穿透。</p>
<h5 id="1-2-缓存穿透的危害"><a href="#1-2-缓存穿透的危害" class="headerlink" title="1.2 缓存穿透的危害"></a>1.2 缓存穿透的危害</h5><p>如果存在海量请求查询压根就不存在的数据，那么这些海量请求都会落到数据库中，数据库压力剧增，可能会导致系统崩溃（你要知道，目前业务系统中最脆弱的就是IO，稍微来点压力它就会崩溃，所以我们要想种种办法保护它）。</p>
<h5 id="1-3-为什么会发生缓存穿透？"><a href="#1-3-为什么会发生缓存穿透？" class="headerlink" title="1.3 为什么会发生缓存穿透？"></a>1.3 为什么会发生缓存穿透？</h5><p>发生缓存穿透的原因有很多，一般为如下两种：</p>
<ol>
<li>恶意攻击，故意营造大量不存在的数据请求我们的服务，由于缓存中并不存在这些数据，因此海量请求均落在数据库中，从而可能会导致数据库崩溃。</li>
</ol>
<ol start="2">
<li>代码逻辑错误。这是程序员的锅，没啥好讲的，开发中一定要避免！<h5 id="1-4-缓存穿透的解决方案"><a href="#1-4-缓存穿透的解决方案" class="headerlink" title="1.4 缓存穿透的解决方案"></a>1.4 缓存穿透的解决方案</h5></li>
</ol>
<p>下面来介绍两种防止缓存穿透的手段。</p>
<h6 id="1-4-1-缓存空数据"><a href="#1-4-1-缓存空数据" class="headerlink" title="1.4.1 缓存空数据"></a>1.4.1 缓存空数据</h6><p>之所以发生缓存穿透，是因为缓存中没有存储这些空数据的key，导致这些请求全都打到数据库上。</p>
<p>那么，我们可以稍微修改一下业务系统的代码，将数据库查询结果为空的key也存储在缓存中。当后续又出现该key的查询请求时，缓存直接返回null，而无需查询数据库。</p>
<h6 id="1-4-2-BloomFilter"><a href="#1-4-2-BloomFilter" class="headerlink" title="1.4.2 BloomFilter"></a>1.4.2 BloomFilter</h6><p>第二种避免缓存穿透的方式即为使用BloomFilter。</p>
<p>它需要在缓存之前再加一道屏障，里面存储目前数据库中存在的所有key,当业务系统有查询请求的时候，首先去BloomFilter中查询该key是否存在。若不存在，则说明数据库中也不存在该数据，因此缓存都不要查了，直接返回null。若存在，则继续执行后续的流程，先前往缓存中查询，缓存中没有的话再前往数据库中的查询。</p>
<h6 id="1-4-3-两种方案的比较"><a href="#1-4-3-两种方案的比较" class="headerlink" title="1.4.3 两种方案的比较"></a>1.4.3 两种方案的比较</h6><p>这两种方案都能解决缓存穿透的问题，但使用场景却各不相同。</p>
<p>对于一些恶意攻击，查询的key往往各不相同，而且数据贼多。此时，第一种方案就显得提襟见肘了。因为它需要存储所有空数据的key，而这些恶意攻击的key往往各不相同，而且同一个key往往只请求一次。因此即使缓存了这些空数据的key，由于不再使用第二次，因此也起不了保护数据库的作用。<br>因此，<strong>对于空数据的key各不相同、<em>key重复请求概率低</em></strong>的场景而言，应该选择第二种方案。而对于<strong>空数据的key数量有限、<em>key重复请求概率较高</em></strong>的场景而言，应该选择第一种方案。</p>
<h5 id="2-缓存雪崩"><a href="#2-缓存雪崩" class="headerlink" title="2. 缓存雪崩"></a>2. 缓存雪崩</h5><h6 id="2-1-什么是缓存雪崩？"><a href="#2-1-什么是缓存雪崩？" class="headerlink" title="2.1 什么是缓存雪崩？"></a>2.1 什么是缓存雪崩？</h6><p>通过上文可知，缓存其实扮演了一个保护数据库的角色。它帮数据库抵挡大量的查询请求，从而避免脆弱的数据库受到伤害。</p>
<p>如果缓存因某种原因发生了宕机，那么原本被缓存抵挡的海量查询请求就会像疯狗一样涌向数据库。此时数据库如果抵挡不了这巨大的压力，它就会崩溃。</p>
<p>这就是缓存雪崩。</p>
<h6 id="2-2-如何避免缓存雪崩？"><a href="#2-2-如何避免缓存雪崩？" class="headerlink" title="2.2 如何避免缓存雪崩？"></a>2.2 如何避免缓存雪崩？</h6><h6 id="2-2-1-使用缓存集群，保证缓存高可用"><a href="#2-2-1-使用缓存集群，保证缓存高可用" class="headerlink" title="2.2.1 使用缓存集群，保证缓存高可用"></a>2.2.1 使用缓存集群，保证缓存高可用</h6><p>也就是在雪崩发生之前，做好预防手段，防止雪崩的发生。</p>
<h6 id="2-2-2-使用Hystrix"><a href="#2-2-2-使用Hystrix" class="headerlink" title="2.2.2 使用Hystrix"></a>2.2.2 使用Hystrix</h6><p>Hystrix是一款开源的“防雪崩工具”，它通过 熔断、降级、限流三个手段来降低雪崩发生后的损失。</p>
<p>Hystrix就是一个Java类库，它采用命令模式，每一项服务处理请求都有各自的处理器。所有的请求都要经过各自的处理器。处理器会记录当前服务的请求失败率。一旦发现当前服务的请求失败率达到预设的值，Hystrix将会拒绝随后该服务的所有请求，直接返回一个预设的结果。这就是所谓的“<strong>熔断</strong>”。当经过一段时间后，Hystrix会放行该服务的一部分请求，再次统计它的请求失败率。如果此时请求失败率符合预设值，则完全打开限流开关；如果请求失败率仍然很高，那么继续拒绝该服务的所有请求。这就是所谓的“<strong>限流</strong>”。而Hystrix向那些被拒绝的请求直接返回一个预设结果，被称为“<strong>降级</strong>”。</p>
<h5 id="3-热点数据集中失效"><a href="#3-热点数据集中失效" class="headerlink" title="3. 热点数据集中失效"></a>3. 热点数据集中失效</h5><h6 id="3-1-什么是热点数据集中失效？"><a href="#3-1-什么是热点数据集中失效？" class="headerlink" title="3.1 什么是热点数据集中失效？"></a>3.1 什么是热点数据集中失效？</h6><p>我们一般都会给缓存设定一个失效时间，过了失效时间后，该数据库会被缓存直接删除，从而一定程度上保证数据的实时性。<br>但是，对于一些请求量极高的热点数据而言，一旦过了有效时间，此刻将会有大量请求落在数据库上，从而可能会导致数据库崩溃。<br>如果某一个热点数据失效，那么当再次有该数据的查询请求[req-1]时就会前往数据库查询。但是，从请求发往数据库，到该数据更新到缓存中的这段时间中，由于缓存中仍然没有该数据，因此这段时间内到达的查询请求都会落到数据库上，这将会对数据库造成巨大的压力。此外，当这些请求查询完成后，都会重复更新缓存。</p>
<h5 id="3-2-解决方案"><a href="#3-2-解决方案" class="headerlink" title="3.2 解决方案"></a>3.2 解决方案</h5><h6 id="3-2-1-互斥锁"><a href="#3-2-1-互斥锁" class="headerlink" title="3.2.1 互斥锁"></a>3.2.1 互斥锁</h6><p>我们可以使用缓存自带的锁机制，当第一个数据库查询请求发起后，就将缓存中该数据上锁；此时到达缓存的其他查询请求将无法查询该字段，从而被阻塞等待；当第一个请求完成数据库查询，并将数据更新值缓存后，释放锁；此时其他被阻塞的查询请求将可以直接从缓存中查到该数据。</p>
<p>当某一个热点数据失效后，只有第一个数据库查询请求发往数据库，其余所有的查询请求均被阻塞，从而保护了数据库。但是，由于采用了互斥锁，其他请求将会阻塞等待，此时系统的吞吐量将会下降。这需要结合实际的业务考虑是否允许这么做。</p>
<p>互斥锁可以避免<strong>某一个</strong>热点数据失效导致数据库崩溃的问题，而在实际业务中，往往会存在一批热点数据同时失效的场景。那么，对于这种场景该如何防止数据库过载呢？</p>
<h6 id="3-3-2-设置不同的失效时间"><a href="#3-3-2-设置不同的失效时间" class="headerlink" title="3.3.2 设置不同的失效时间"></a>3.3.2 设置不同的失效时间</h6><p>当我们向缓存中存储这些数据的时候，可以将他们的缓存失效时间错开。这样能够避免同时失效。如：在一个基础时间上加/减一个随机数，从而将这些缓存的失效时间错开。</p>
<p><img src="https://sxk.s3.didiyunapi.com/pic/redis/redis.jpg" alt="image"></p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/03/11/zookeeper/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/03/11/zookeeper/" itemprop="url">zookeeper</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-03-11T09:30:52+08:00">
                2018-03-11
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <blockquote>
<p>@Author:<a href="mailto:songxiankun2008@126.com" target="_blank" rel="noopener">songxiankun2008@126.com</a></p>
</blockquote>
<h5 id="1-分布式面临问题-CAP-BASE理论-zookeeper能做什么"><a href="#1-分布式面临问题-CAP-BASE理论-zookeeper能做什么" class="headerlink" title="1.分布式面临问题 CAP/BASE理论 zookeeper能做什么"></a>1.分布式面临问题 CAP/BASE理论 zookeeper能做什么</h5><hr>
<p>ACID 事务 单机数据库的 一致性</p>
<p>跨库事务 分布式事务 尽量避免</p>
<h6 id="CAP理论："><a href="#CAP理论：" class="headerlink" title="CAP理论："></a>CAP理论：</h6><ul>
<li>Consistency（一致性）<ul>
<li>一致性（C）：在分布式系统中的所有数据备份，在同一时刻是否同样的值。（等同于所有节点访问同一份最新的数据副本）</li>
</ul>
</li>
<li>Availability（可用性）<ul>
<li>可用性（A）：在集群中一部分节点故障后，集群整体是否还能响应客户端的读写请求。（对数据更新具备高可用性）</li>
</ul>
</li>
<li><p>Partition tolerance（分区容错性）</p>
<ul>
<li>分区容忍性（P）：以实际效果而言，分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性，就意味着发生了分区的情况，必须就当前操作在C和A之间做出选择。</li>
</ul>
</li>
<li><p>[x] 最多只能取其中两个  最多只能满足其中两个 不可能满足三个;如果是强一致性,就要牺牲可分片</p>
</li>
</ul>
<h6 id="BASE理论："><a href="#BASE理论：" class="headerlink" title="BASE理论："></a>BASE理论：</h6><p>==BASE==是指基本可用（Basically Available）、软状态（ Soft State）、最终一致性（ Eventual Consistency）。</p>
<ul>
<li><p>基本可用（Basically Available）</p>
<ul>
<li>基本可用是指分布式系统在出现故障的时候，允许损失部分可用性，即保证核心可用。</li>
<li>电商大促时，为了应对访问量激增，部分用户可能会被引导到降级页面，服务层也可能只提供降级服务。这就是损失部分可用性的体现。</li>
</ul>
</li>
<li><p>软状态（ Soft State）</p>
<ul>
<li>软状态是指允许系统存在中间状态，而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本，允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。</li>
</ul>
</li>
<li><p>最终一致性（ Eventual Consistency）</p>
<ul>
<li>最终一致性是指系统中的所有数据副本经过一定时间后，最终能够达到一致的状态。弱一致性和强一致性相反，最终一致性是弱一致性的一种特殊情况。</li>
</ul>
</li>
<li><p>弱状态</p>
<ul>
<li>(相当于是数据库的主从复制,这个时候主从节点的数据是不一致的所以称为弱状态,虽然最终结果数据会一致) </li>
</ul>
</li>
<li>最终一致性(表示数据暂时不一致,但是数据最终会一致的) <ul>
<li>最终一致性是弱一致性的一个特例，系统会保证在一定时间内，能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来，是因为它是弱一致性中非常推崇的一种一致性模型，也是业界在大型分布式系统的数据一致性上比较推崇的模型   </li>
</ul>
</li>
<li>强一致性<ul>
<li>这种一致性级别是最符合用户直觉的，它要求系统写入什么，读出来的也会是什么，用户体验好，但实现起来往往对系统的性能影响大 </li>
</ul>
</li>
<li>弱一致性<ul>
<li>这种一致性级别约束了系统在写入成功后，不承诺立即可以读到写入的值，也不久承诺多久之后数据能够达到一致，但会尽可能地保证到某个时间级别（比如秒级别）后，数据能够达到一致状态</li>
</ul>
</li>
</ul>
<h6 id="ACID和BASE的区别与联系"><a href="#ACID和BASE的区别与联系" class="headerlink" title="ACID和BASE的区别与联系"></a>ACID和BASE的区别与联系</h6><ul>
<li>ACID是传统数据库常用的设计理念，追求强一致性模型。BASE支持的是大型分布式系统，提出通过牺牲强一致性获得高可用性。</li>
<li>ACID和BASE代表了两种截然相反的设计哲学，在分布式系统设计的场景中，系统组件对一致性要求是不同的，因此ACID和BASE又会结合使用。</li>
</ul>
<h6 id="zookeeper功能"><a href="#zookeeper功能" class="headerlink" title="zookeeper功能:"></a>zookeeper功能:</h6><ul>
<li>dubbo 服务治理框架 核心zk</li>
<li>发布订阅：redis mq zookeeper </li>
<li>负载均衡: ngix apache-http</li>
<li>命名服务：</li>
<li>分布式协调通知：</li>
<li>集群管理：</li>
<li>==Master选举==：</li>
<li>分布式锁: redis </li>
<li>分布式队列</li>
</ul>
<h6 id="分布式系统八大谬论"><a href="#分布式系统八大谬论" class="headerlink" title="分布式系统八大谬论:"></a>分布式系统八大谬论:</h6><ul>
<li>网络相当可靠</li>
<li>延迟为零</li>
<li>传输带宽是无限的</li>
<li>网络相当安全</li>
<li>拓扑结构不会改变</li>
<li>必须要有一名管理员</li>
<li>传输成本为零</li>
<li>网络同质化<h5 id="2-各种一致性协议解释"><a href="#2-各种一致性协议解释" class="headerlink" title="2.各种一致性协议解释"></a>2.各种一致性协议解释</h5></li>
</ul>
<hr>
<h6 id="高可用-一致性-取舍"><a href="#高可用-一致性-取舍" class="headerlink" title="高可用 一致性 取舍"></a>高可用 一致性 取舍</h6><p>一致性： 分布式事务 强一致性 2PC(2阶段提交) 3PC(3阶段提交)</p>
<p>高可用性 集群 服务拆分 ： 主从结构 Master选举<br>每个从节点保存的数据 不一致</p>
<p>Paxos算法:一致性协议</p>
<p>zookeeper的核心是一个选举算法(Paxos算法)，将各方面达成一致，几阶段提交那个的都不是重点</p>
<p><img src="E:/YoudaoNote/pic/zookeeper/1.jpg" alt="image"></p>
<ul>
<li>上图是 2阶段提交的模型;下图是出现“事务中断”的时候全部回滚的模型(==*引入协调者进行凉着或者多个参与者进行协调==)<ul>
<li>缺点：单点故障(协调者只有一个,如果协调者挂掉)</li>
</ul>
</li>
</ul>
<p><img src="E:/YoudaoNote/pic/zookeeper/2.jpg" alt="image"></p>
<ul>
<li>下图是 3阶段提交的模型(每次都要询问是否准备好要开始提交)</li>
</ul>
<p><img src="E:/YoudaoNote/pic/zookeeper/3.jpg" alt="image"></p>
<p>在Java中的容器支持：</p>
<ul>
<li>JTA(java Transtion..)  </li>
<li><p>JBose(类似于Tomcat的一种容器)天生不支持JTA，但是引入一个包(JOTM 一个开源JTA实现)就可以</p>
</li>
<li><p>NWR(协议)： </p>
<ul>
<li>N（数据分片 节点） (下图有3个节点)</li>
<li>W 每次写入 写入多少阶段算成功  (根据下图,每次至少写入两个算成功)</li>
<li>R 每次读取 读取多少阶段算成功(根据下图,每次至少读取两个算成功)</li>
<li>在三个节点中,写两次读两次总会有一条数据是强一致性即总会读到正确的数据</li>
</ul>
</li>
</ul>
<p><img src="E:/YoudaoNote/pic/zookeeper/4.jpg" alt="image"></p>
<p>NWR值的不同组合会产生不同的一致性效果,当W+R&gt;N的时候,整个系统对于客户端来讲能保证强一致性;</p>
<p>兰伯特 推出Paxos 90年发表出来但是不想解释 2001年才勉强解释这个算法： Paxos made simple </p>
<p>在paxos算法中，分为4种角色：<br>  Proposer ：提议者<br>  Acceptor：决策者<br>  Client：产生议题者<br>  Learner：最终决策学习者</p>
<p><img src="E:/YoudaoNote/pic/zookeeper/5.jpg" alt="image"></p>
<p><strong>提案</strong>： 【编号 ， 值】 [K,V]</p>
<p>==规则==：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Acceptor：决策者 必须接受第一提案；</span><br><span class="line">    Acceptor：决策者 不能接受比K值小的提案</span><br><span class="line">    Proposer：提议者 必须获得多数节点的支持 否则就得继续提案直到获得多数支持</span><br><span class="line">  决议阶段： 如果有比k大的k+1值，那么就只能接受k+1的值，不能接受比(k+1)的值</span><br></pre></td></tr></table></figure>
<h6 id="Paxos协议分为两个阶段："><a href="#Paxos协议分为两个阶段：" class="headerlink" title="Paxos协议分为两个阶段："></a>Paxos协议分为两个阶段：</h6><p><em>此处有4个步骤…</em></p>
<ul>
<li>准备阶段</li>
<li>决议阶段</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">step1：，设置时钟：proposer令localClock=globalClock.incrementAndGet()。(全局时钟设置,这个是按时间顺序的,编号越后来的值越大)</span><br><span class="line"></span><br><span class="line">step2：prepare:proposer向所有Acceptor发送一个prepare消息。</span><br><span class="line">       接收方应返回它最近一次accept的value，以及 accept的时间，若在它还没有accept过value，那么就返回空。</span><br><span class="line">       proposer只有在收到过半数的response之后，才可进入下一个阶段。一旦收到reject消息，那么就重头来。</span><br><span class="line">       (超过半数即：如果有5个节点,要3个通过才算通过;如果有3个节点,2个通过算通过)</span><br><span class="line"></span><br><span class="line">step3：构造Proposal:proposer从prepare阶段收到的所有values中选取时间戳最新的一个。如果没有，那么它自己提议一个value。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">step4：发送Proposal:proposer把value发送给其它所有机器，消息的时间戳取自localClock。</span><br><span class="line">       接收方只要检查消息时间戳合法，那么就接受此value，把这个value和时间戳写入到硬盘上，然后答复OK，否则拒绝接受。</span><br><span class="line">       proposer若收到任何的reject答复，则回到 step1。否则，在收到过半数的OK后，此Proposal被通过</span><br></pre></td></tr></table></figure>
<p><strong>Phase1（准备阶段）</strong></p>
<ul>
<li style="list-style: none"><input type="checkbox" checked> 1. 每个Server都向Proposer发消息称自己要成为leader，Server1往Proposer1发、Server2往Proposer2发、Server3往Proposer3发；</li>
<li style="list-style: none"><input type="checkbox" checked> 2. 现在每个Proposer都接收到了Server1发来的消息但时间不一样，Proposer2先接收到了，然后是Proposer1，接着才是Proposer3；</li>
<li><p>[x] 3. Proposer2首先接收到消息所以他从系统中取得一个编号1，Proposer2向Acceptor2和Acceptor3发送一条，编号为1的消息；接着Proposer1也接收到了Server1发来的消息，取得一个编号2，Proposer1向Acceptor1和Acceptor2发送一条，编号为2的消息； 最后Proposer3也接收到了Server3发来的消息，取得一个编号3，Proposer3向Acceptor2和Acceptor3发送一条，编号为3的消息；</p>
</li>
<li><p>[x] 1. 这时Proposer1发送的消息先到达Acceptor1和Acceptor2，这两个都没有接收过请求所以接受了请求返回[2,null]给Proposer1，并承诺不接受编号小于2的请求；</p>
</li>
<li style="list-style: none"><input type="checkbox" checked> 2. 此时Proposer2发送的消息到达Acceptor2和Acceptor3，Acceprot3没有接收过请求返回[1,null]给Proposer2，并承诺不接受编号小于1的请求，但这时Acceptor2已经接受过Proposer1的请求并承诺不接受编号小于的2的请求了，所以Acceptor2拒绝Proposer2的请求；</li>
<li style="list-style: none"><input type="checkbox" checked> 3. 最后Proposer3发送的消息到达Acceptor2和Acceptor3，Acceptor2接受过提议，但此时编号为3大于Acceptor2的承诺2与Accetpor3的承诺1，所以接受提议返回[3,null];</li>
<li style="list-style: none"><input type="checkbox" checked> 4. Proposer2没收到过半的回复所以重新取得编号4，并发送给Acceptor2和Acceptor3，然后Acceptor2和Acceptor3通过<br><strong>Phase2（决议阶段）</strong></li>
<li style="list-style: none"><input type="checkbox" checked> 1. Proposer3收到过半（三个Server中两个）的返回，并且返回的Value为null，所以Proposer3提交了[3,server3]的议案；</li>
<li style="list-style: none"><input type="checkbox" checked> 2. Proposer1收到过半返回，返回的Value为null，所以Proposer1提交了[2,server1]的议案；</li>
<li style="list-style: none"><input type="checkbox" checked> 3. Proposer2收到过半返回，返回的Value为null，所以Proposer2提交了[4,server2]的议案；</li>
<li style="list-style: none"><input type="checkbox" checked> 4. Acceptor1、Acceptor2接收到Proposer1的提案[2,server1]请求，Acceptor2承诺编号大于4所以拒绝了通过，Acceptor1通过了请求；</li>
<li style="list-style: none"><input type="checkbox" checked> 5. Proposer2的提案[4,server2]发送到了Acceptor2、Acceptor3，提案编号为4所以Acceptor2、Acceptor3都通过了提案请求；</li>
<li style="list-style: none"><input type="checkbox" checked> 6. Acceptor2、Acceptor3接收到Proposer3的提案[3,server3]请求，Acceptor2、Acceptor3承诺编号大于4所以拒绝了提案；</li>
<li style="list-style: none"><input type="checkbox" checked> 7. 此时过半的Acceptor都接受了Proposer2的提案[4,server2],Larner感知到了提案的通过，Larner学习提案，server2成为Leader；</li>
</ul>
<p><img src="E:/YoudaoNote/pic/zookeeper/6.jpg" alt="image"></p>
<p>参考博客：</p>
<p><a href="http://linbingdong.com/2017/04/17/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0%E2%80%94%E2%80%94Paxos%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86%E4%B8%8E%E6%8E%A8%E5%AF%BC/" target="_blank" rel="noopener">http://linbingdong.com/2017/04/17/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0%E2%80%94%E2%80%94Paxos%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86%E4%B8%8E%E6%8E%A8%E5%AF%BC/</a></p>
<p><a href="https://segmentfault.com/a/1190000005717258" target="_blank" rel="noopener">https://segmentfault.com/a/1190000005717258</a></p>
<p><img src="E:/YoudaoNote/pic/zookeeper/7.jpg" alt="image"></p>
<h5 id="3-zookeeper原理-安装使用"><a href="#3-zookeeper原理-安装使用" class="headerlink" title="3. zookeeper原理 安装使用"></a>3. zookeeper原理 安装使用</h5><hr>
<ul>
<li><p>1.顺序一致性(不管有多少个分布式单位请求这个zookeeper，zookeeper处理他们的顺序一定是按照他们请求的顺序来的不会乱序；在真正分布式环境下达到顺序是很难保证的)</p>
</li>
<li><p>2.原子性(要么都把数据插入成功，要么都失败，不会有脏数据产生)</p>
</li>
<li><p>3.单一视图(无论有多少个客户连接了这个东西，那么所有的用户看到的这个界面都是一样的)</p>
</li>
<li><p>4.可靠性(相当于是ACID里面的持久性)</p>
</li>
<li><p>5.实时性(一个数据操作成功，用户就能立即刷新到数据，但是zookeeper保证的是在一定时间之内，是最终强一致性(最终强一致性的代价是效率的牺牲)</p>
</li>
<li><p>[x] zookeeper自身的安全性保障：==zookeeper本身就是一个分布式系统==(zookeeper自身就是一个单点故障问题) 一般部署奇数个zk服务器(奇数个是因为，少数服从多数，比如出现2:2的情况就只能无限循环下去，一般都是部署3,5台(为了高可用 可能会部署多台机器，就很难保证顺序一致性))</p>
</li>
</ul>
<ul>
<li style="list-style: none"><input type="checkbox" checked> paxos核心精髓 ，但是zk并没有直接采用paxos ，而是用了一种其他的算法,一套Zab算法,人们认为这种算法是基于paxos再改造的算法</li>
</ul>
<p>zab:  </p>
<ul>
<li><ol>
<li>第一：①原子广播((一直都只有一个Master,其他全部为slave))  ②数据同步(有了主从结构就会有这个数据同步) ③全局事务ID(根据这个ID来进行数据同步) </li>
</ol>
</li>
<li><ol start="2">
<li>第二： <strong>Master选举</strong><ul>
<li><ol>
<li>basic-paxos算法(模仿paxos算法)</li>
</ol>
</li>
<li><ol start="2">
<li>fast-paxos改进了basic-paxos算法</li>
</ol>
</li>
<li><ol start="3">
<li>zookeeper现在默认的是fast-paxos算法，当然可以把zk的算法改成basic-paxos算法<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">上面两个重点：</span><br><span class="line">1是Master选举,自身就是一个分布式集群;</span><br><span class="line">2是原子广播:要让所有地方的数据保持一致;跟“数据同步”“全局事务ID息息相关”</span><br></pre></td></tr></table></figure>
</li>
</ol>
</li>
</ul>
</li>
</ol>
</li>
</ul>
<h6 id="zookeeper如果是安装单机状态-就很容易保证上面的那5个特性-但是为了高可用就要用zookeeper来搭集群"><a href="#zookeeper如果是安装单机状态-就很容易保证上面的那5个特性-但是为了高可用就要用zookeeper来搭集群" class="headerlink" title="zookeeper如果是安装单机状态,就很容易保证上面的那5个特性;但是为了高可用就要用zookeeper来搭集群"></a>zookeeper如果是安装单机状态,就很容易保证上面的那5个特性;但是为了高可用就要用zookeeper来搭集群</h6><ol>
<li>复制三份zookeeper,并将各个conf里面的zoo_sample.cfg重命名为zoo.cfg表示配置启用</li>
<li>在第一个根目录下新建data文件夹用来存放数据,配置在下面的代码</li>
<li>修改第一个文件夹里面的zoo.cfg</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"># The number of milliseconds of each tick</span><br><span class="line">#心跳检测 2000ms</span><br><span class="line">tickTime=2000</span><br><span class="line"># The number of ticks that the initial </span><br><span class="line"># synchronization phase can take</span><br><span class="line">#初始化：这儿的10*2000得到的时间还是没有连接上的话就默认断开或者连接失败</span><br><span class="line">initLimit=10</span><br><span class="line"># The number of ticks that can pass between </span><br><span class="line"># sending a request and getting an acknowledgement</span><br><span class="line">#与Master跟slave之间或者slave与slave之间进行数据同步的时候5*2000</span><br><span class="line">#得到的时间内如果对方没有回应,或(ACK:acknowledgement)机制反馈的话就表明挂掉了</span><br><span class="line">#此时就可能需要新的投票什么的</span><br><span class="line">syncLimit=5</span><br><span class="line"># the directory where the snapshot is stored.</span><br><span class="line"># do not use /tmp for storage, /tmp here is just </span><br><span class="line"># example sakes.</span><br><span class="line">dataDir=G:/Plugins/zookeeper-Group/zookeeper-3.4.10-1/data</span><br><span class="line"># the port at which the clients will connect</span><br><span class="line">#后面的集群端口号要不一致</span><br><span class="line">clientPort=2181</span><br><span class="line"># the maximum number of client connections.</span><br><span class="line"># increase this if you need to handle more clients</span><br><span class="line">#maxClientCnxns=60</span><br><span class="line">#</span><br><span class="line"># Be sure to read the maintenance section of the </span><br><span class="line"># administrator guide before turning on autopurge.</span><br><span class="line">#</span><br><span class="line"># http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance</span><br><span class="line">#</span><br><span class="line"># The number of snapshots to retain in dataDir</span><br><span class="line">#autopurge.snapRetainCount=3</span><br><span class="line"># Purge task interval in hours</span><br><span class="line"># Set to &quot;0&quot; to disable auto purge feature</span><br><span class="line">#autopurge.purgeInterval=1</span><br><span class="line">#此处是新增的</span><br><span class="line">server.1=localhost:2887:3887</span><br><span class="line">server.2=localhost:2888:3888</span><br><span class="line">server.3=localhost:2889:3889</span><br><span class="line">#此处是在本机搭建了3台集群,</span><br><span class="line">#localhost表示这个节点的ip,</span><br><span class="line">#第一个端口表明数据同步的时候的ip,</span><br><span class="line">#第二个ip表示如果服务器挂掉后进行Master选举的时候要用的端口</span><br></pre></td></tr></table></figure>
<ol start="4">
<li>将第二个第三个文件夹也类似修改配置,只是客户端的端口号不一致,存放数据data的文件夹的路径不一致;其他一致</li>
<li>分别在第一个第二个第三个文件夹的data文件夹中新建文件“myid(无后缀)”,在里面写上这是第几个节点,即里面的内容分别为1、 2、 3</li>
<li>启动：顺次进入第一二三的bin文件夹里面启zkServer.cmd(共启动三个zkServer.cmd)</li>
</ol>
<p><img src="E:/YoudaoNote/pic/zookeeper/8.jpg" alt="image"></p>
<h6 id="上图表示服务治理-剔除无效服务-节点中可以再有子节点-的模型"><a href="#上图表示服务治理-剔除无效服务-节点中可以再有子节点-的模型" class="headerlink" title="上图表示服务治理(剔除无效服务,节点中可以再有子节点)的模型;"></a>上图表示服务治理(剔除无效服务,节点中可以再有子节点)的模型;</h6><ol start="7">
<li>三个启动好了后,进入第一个文件夹的bin里面启动zkCli.cmd;启动好了后如图：</li>
</ol>
<p><img src="E:/YoudaoNote/pic/zookeeper/9.jpg" alt="image"></p>
<ol start="8">
<li>右下角的框里出现那样的会话表示客户端也已经启动成功,输入</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create /zk-node laowang</span><br></pre></td></tr></table></figure>
<p>这句命令后面还可以跟上其他命令比如权限什么的</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ls /     --- “ls /”命令是查看当前根目录下面有哪些节点</span><br><span class="line"></span><br><span class="line">get /    ---   查看所有的节点数</span><br><span class="line"></span><br><span class="line">get /zk-node    --- 此命令是获取节点名为“ zk-node”的信息</span><br><span class="line"></span><br><span class="line">set /laowang2   ---  将当前节点中的信息(laowang)修改为laowang2</span><br><span class="line"></span><br><span class="line">delete /zk-node   --- 删除当前这个节点</span><br></pre></td></tr></table></figure>
<p>以上是在命令行中这样使用,在Java中使用是提供了一套工具包：==zkClient工具包==;如果是在代码中,在zookeeper里面有自带zookeeper.jar</p>
<h6 id="zk节点："><a href="#zk节点：" class="headerlink" title="zk节点："></a>zk节点：</h6><ul>
<li>持久节点 </li>
<li>临时节点(会话断开就消失，根据这个特点就可以服务治理，节点下面可以有数据(少量)，可以监听节点的变化，客户端可以拿到通知(拿到配置分发的实现))(配置管理原理)<br>节点名称是唯一的(分布式锁的原理，谁先来谁先得到)<br>节点可以有顺序，像是乐观锁和悲观锁 而且是分布式的</li>
</ul>
<h6 id="在Java代码中连接zookeeper-并且连接上-此处是持久化连接-若是临时接点可能一会儿就消失了不便于操作"><a href="#在Java代码中连接zookeeper-并且连接上-此处是持久化连接-若是临时接点可能一会儿就消失了不便于操作" class="headerlink" title="在Java代码中连接zookeeper,并且连接上(此处是持久化连接,若是临时接点可能一会儿就消失了不便于操作)"></a>在Java代码中连接zookeeper,并且连接上(此处是持久化连接,若是临时接点可能一会儿就消失了不便于操作)</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">public class Application &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        //地址与端口配置</span><br><span class="line">        String zkServers = &quot;localhost:2181,localhost:2182,localhost:2183&quot;;</span><br><span class="line">        //加载地址;session保存时间;首次连接超时时间;序列化(节点什么传输(其他地方,Java在网络上传输对象要实现序列化与反序列化))</span><br><span class="line">        ZkClient zkClient = new ZkClient(zkServers, 10000, 10000, new SerializableSerializer());</span><br><span class="line">        System.out.println(&quot;connection OK&quot;);</span><br><span class="line"></span><br><span class="line">        User user = new User();</span><br><span class="line">        user.setId(1);</span><br><span class="line">        user.setName(&quot;songxiankun&quot;);</span><br><span class="line"></span><br><span class="line">        //配置节点 (模式有持久节点,临时节点等;ACL是权限配置)</span><br><span class="line">        String path = zkClient.create(&quot;/testNode&quot;,user, CreateMode.PERSISTENT);</span><br><span class="line">        //PERSISTENT:持久化;PERSISTENT_SEQUENTIAL:持久化序列;EPHEMERAL:临时节点</span><br><span class="line">        System.out.println(&quot;创建节点:&quot;+path);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>查看是否有这个节点：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Boolean b = zkClient.exists(&quot;/testNode&quot;);</span><br><span class="line">System.out.println(&quot;是否存在节点:&quot; + b);</span><br></pre></td></tr></table></figure></p>
<p>获取：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">User user = zkClient.readData(&quot;/testNode&quot;);</span><br><span class="line">    //之前指定了序列化,此处是反序列化成对象</span><br><span class="line">System.out.println(user.getName());</span><br></pre></td></tr></table></figure></p>
<p>删除：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zkClient.delete(&quot;/testNode&quot;);</span><br></pre></td></tr></table></figure></p>
<p>数据更新修改：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">zkClient.writeData(&quot;/testNode&quot;, user);  </span><br><span class="line">//将信息包装成对象再放进去</span><br></pre></td></tr></table></figure>
<h6 id="监控节点-数量-变化："><a href="#监控节点-数量-变化：" class="headerlink" title="监控节点(数量)变化："></a>监控节点(数量)变化：</h6><ul>
<li>创建节点并加入节点监听机制：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">User user = new User();</span><br><span class="line">    user.setId(2);</span><br><span class="line">    user.setName(&quot;/ppppppppppppp&quot;);</span><br><span class="line"></span><br><span class="line">    String path = zkClient.create(&quot;/twiceNode&quot;, user, CreateMode.PERSISTENT);</span><br><span class="line">    System.out.println(&quot;创建节点:&quot; + path);</span><br><span class="line">    zkClient.subscribeChildChanges(&quot;/twiceNode&quot;, new ZkListener());</span><br><span class="line">    Thread.sleep(Integer.MAX_VALUE);</span><br></pre></td></tr></table></figure>
<ul>
<li>配置监听(实现节点监听):</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public class ZkListener implements IZkChildListener &#123;</span><br><span class="line">    /** 监听子节点的变化 */</span><br><span class="line">    public void handleChildChange(String parentPath, List&lt;String&gt; Children) throws Exception &#123;</span><br><span class="line">        System.out.println(parentPath);</span><br><span class="line">        /**监听子节点上面的数量变化*/</span><br><span class="line">        System.out.println(Children.toString());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>此时,在客户端根目录下输入命令：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">create /twiceNode/node1 123</span><br><span class="line">//解释:在节点下再创子节点： “twiceNode”在此处表示是父节点,是必须已经存在的,“node1”是新即将创建的子节点,“123”表示这个新的子节点上面带的值</span><br></pre></td></tr></table></figure>
<ul>
<li>IDEA输出：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">/twiceNode</span><br><span class="line">[node1]</span><br><span class="line">//解释: 在父目录:/twiceNode中 成功创建了子节点“node1”</span><br></pre></td></tr></table></figure>
<p><img src="E:/YoudaoNote/pic/zookeeper/10.jpg" alt="image"></p>
<h6 id="监控节点-内容-变化："><a href="#监控节点-内容-变化：" class="headerlink" title="监控节点(内容)变化："></a>监控节点(内容)变化：</h6><ul>
<li>创建节点并加入数据监听机制：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">User user = new User();</span><br><span class="line">        user.setId(4);</span><br><span class="line">        user.setName(&quot;/yyyyyyyyyyyy&quot;);</span><br><span class="line"></span><br><span class="line">        String path = zkClient.create(&quot;/FourNode&quot;, user, CreateMode.PERSISTENT);</span><br><span class="line">        System.out.println(&quot;创建节点:&quot; + path);</span><br><span class="line">        zkClient.subscribeDataChanges(&quot;/FourNode&quot;, new ZkDataListener());</span><br><span class="line">        Thread.sleep(3000);</span><br><span class="line">        zkClient.writeData(&quot;/FourNode&quot;, 456);</span><br><span class="line">        Thread.sleep(Integer.MAX_VALUE);</span><br></pre></td></tr></table></figure>
<ul>
<li>配置监听(实现内容监听):</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">public class ZkDataListener implements IZkDataListener &#123;</span><br><span class="line">    /** 数据发生修改的话的操作 */</span><br><span class="line">    public void handleDataChange(String dataPath, Object data) throws Exception &#123;</span><br><span class="line">        System.out.println(dataPath+&quot;-------&quot;+data);</span><br><span class="line">    &#125;</span><br><span class="line">    /** 数据被删除的话的操作 */</span><br><span class="line">    public void handleDataDeleted(String s) throws Exception &#123;</span><br><span class="line">        System.out.println(s);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>当启动项目(运行主函数)后的效果：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">创建节点:/FourNode</span><br><span class="line">/FourNode-------456</span><br><span class="line">//解释：第一行表示创建节点成功;第二行表示上面zkClient.writeData(&quot;/FourNode&quot;, 456);内容修改被监控打印</span><br></pre></td></tr></table></figure>
<ul>
<li>在命令行端修改内容：set /FourNode 789 不会有任何反应,是因为这个修改值要想有反应的话只有在代码中才可以;在命令行解密修改值是不会被监听打印到的</li>
<li>在命令行删除：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">客户端输入     ： delete /FourNode</span><br><span class="line">IDEA被监控-打印：   /FourNode</span><br></pre></td></tr></table></figure>
<h4 id="4-zookeeper各种应用实践场景"><a href="#4-zookeeper各种应用实践场景" class="headerlink" title="4. zookeeper各种应用实践场景"></a>4. zookeeper各种应用实践场景</h4><h6 id="开源的分布式配置管理："><a href="#开源的分布式配置管理：" class="headerlink" title="开源的分布式配置管理："></a>开源的分布式配置管理：</h6><ul>
<li>百度的disconf </li>
<li><p>淘宝的diamond </p>
</li>
<li><p>[ ] 其实这两个都是浮云，后来阿里有了dubbo，然后当当网改进了dubbox(这两个都是专业于服务治理的);</p>
</li>
<li><p>[x] 然后现在有个一个全家桶的概念：SpringCloud ， 已经把dubbo(x)完全取代甩了几条大街了，什么网关啊服务代理啊什么东西之类的完全都有的(业界顶尖方案)</p>
</li>
</ul>
<h6 id="分布式治理："><a href="#分布式治理：" class="headerlink" title="分布式治理："></a>分布式治理：</h6><p><img src="E:/YoudaoNote/pic/zookeeper/11.jpg" alt="image"></p>
<p><strong>Master选举</strong></p>
<p><strong>分布式锁： 排他锁 共享锁</strong></p>
<ul>
<li>排他锁：是将其他锁阻塞在线程之外</li>
<li>共享锁： 允许其他锁来读，但是写的话还是要排队</li>
</ul>
<p>共享锁会涉及到 有序节点，在这个节点之中，zookeeper会自动给每个节点分配一个编号，这个编号是先来的会小；<br>如果这个节点(编号)前面有比这个编号还要小的节点的话，如果它在读，那么自己就放心的读，如果它在写，那么就只能等这个小的编号写完了，自己的这个大的编号才能够去读或者写</p>
<p>分布式队列：先进先出,完成任务后就删除</p>
<h6 id="zookeeper的节点："><a href="#zookeeper的节点：" class="headerlink" title="zookeeper的节点："></a>zookeeper的节点：</h6><ul>
<li style="list-style: none"><input type="checkbox" checked> ZooKeeper 节点是有生命周期的，这取决于节点的类型。</li>
<li>在 ZooKeeper中，节点类型可以分为持久节点（PERSISTENT ）、临时节点（EPHEMERAL），以及时序节点（SEQUENTIAL ），具体在节点创建过程中，一般是组合使用，可以生成以下 4 种节点类型。</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">1. 持久节点（PERSISTENT）</span><br><span class="line">2. 持久顺序节点（PERSISTENT_SEQUENTIAL）</span><br><span class="line">3. 临时节点（EPHEMERAL）</span><br><span class="line">4. 临时顺序节点（EPHEMERAL_SEQUENTIAL）</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">安装使用：</span><br><span class="line"></span><br><span class="line">1.在官网下载zookeeper稳定版(windows 和 linux 版本是通用的)</span><br><span class="line">2.复制成3分，并重命名123</span><br><span class="line">3.在根文件夹下文件夹data</span><br><span class="line">4.在conf文件夹下将zoo-simple.cfg重命名为zoo.cfg</span><br><span class="line">5.修改zoo.xml里面的端口号，3分分别为2181,2182,2183</span><br><span class="line">6.修改3分的datadir文件夹路径分别为三个不同的data路径</span><br><span class="line">7.添加集群配置文件:server.1=localhost:2887:3887</span><br><span class="line">server.2=localhost:2888:3889</span><br><span class="line">server.3=localhost:2889:3889</span><br><span class="line">#这个地方是集群配置</span><br><span class="line">#前面的端口号是连接端口号</span><br><span class="line">#后面的端口号是Master选举的端口号</span><br><span class="line">8.在每个data文件夹下新建文件myid,里面写入分别是1,2,3</span><br><span class="line">9.在启动的时候，3分都要启动才能启动成功</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/03/08/work-attendance/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/03/08/work-attendance/" itemprop="url">work-attendance</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-03-08T09:30:52+08:00">
                2018-03-08
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br><span class="line">584</span><br><span class="line">585</span><br><span class="line">586</span><br><span class="line">587</span><br><span class="line">588</span><br><span class="line">589</span><br><span class="line">590</span><br><span class="line">591</span><br><span class="line">592</span><br><span class="line">593</span><br><span class="line">594</span><br><span class="line">595</span><br><span class="line">596</span><br><span class="line">597</span><br><span class="line">598</span><br><span class="line">599</span><br><span class="line">600</span><br><span class="line">601</span><br><span class="line">602</span><br><span class="line">603</span><br><span class="line">604</span><br><span class="line">605</span><br><span class="line">606</span><br><span class="line">607</span><br><span class="line">608</span><br><span class="line">609</span><br><span class="line">610</span><br><span class="line">611</span><br><span class="line">612</span><br><span class="line">613</span><br><span class="line">614</span><br><span class="line">615</span><br><span class="line">616</span><br><span class="line">617</span><br><span class="line">618</span><br><span class="line">619</span><br><span class="line">620</span><br><span class="line">621</span><br><span class="line">622</span><br><span class="line">623</span><br><span class="line">624</span><br><span class="line">625</span><br><span class="line">626</span><br><span class="line">627</span><br><span class="line">628</span><br><span class="line">629</span><br><span class="line">630</span><br><span class="line">631</span><br><span class="line">632</span><br><span class="line">633</span><br><span class="line">634</span><br><span class="line">635</span><br><span class="line">636</span><br><span class="line">637</span><br><span class="line">638</span><br><span class="line">639</span><br><span class="line">640</span><br><span class="line">641</span><br><span class="line">642</span><br><span class="line">643</span><br><span class="line">644</span><br><span class="line">645</span><br><span class="line">646</span><br><span class="line">647</span><br><span class="line">648</span><br><span class="line">649</span><br><span class="line">650</span><br><span class="line">651</span><br><span class="line">652</span><br><span class="line">653</span><br><span class="line">654</span><br><span class="line">655</span><br><span class="line">656</span><br><span class="line">657</span><br><span class="line">658</span><br><span class="line">659</span><br><span class="line">660</span><br><span class="line">661</span><br><span class="line">662</span><br><span class="line">663</span><br><span class="line">664</span><br><span class="line">665</span><br><span class="line">666</span><br><span class="line">667</span><br><span class="line">668</span><br><span class="line">669</span><br><span class="line">670</span><br><span class="line">671</span><br><span class="line">672</span><br><span class="line">673</span><br><span class="line">674</span><br><span class="line">675</span><br><span class="line">676</span><br><span class="line">677</span><br><span class="line">678</span><br><span class="line">679</span><br><span class="line">680</span><br><span class="line">681</span><br><span class="line">682</span><br><span class="line">683</span><br><span class="line">684</span><br><span class="line">685</span><br><span class="line">686</span><br><span class="line">687</span><br><span class="line">688</span><br><span class="line">689</span><br><span class="line">690</span><br><span class="line">691</span><br><span class="line">692</span><br><span class="line">693</span><br><span class="line">694</span><br><span class="line">695</span><br><span class="line">696</span><br><span class="line">697</span><br><span class="line">698</span><br><span class="line">699</span><br><span class="line">700</span><br><span class="line">701</span><br><span class="line">702</span><br><span class="line">703</span><br><span class="line">704</span><br><span class="line">705</span><br><span class="line">706</span><br><span class="line">707</span><br><span class="line">708</span><br><span class="line">709</span><br><span class="line">710</span><br><span class="line">711</span><br><span class="line">712</span><br><span class="line">713</span><br><span class="line">714</span><br><span class="line">715</span><br><span class="line">716</span><br><span class="line">717</span><br><span class="line">718</span><br><span class="line">719</span><br><span class="line">720</span><br><span class="line">721</span><br><span class="line">722</span><br><span class="line">723</span><br><span class="line">724</span><br><span class="line">725</span><br><span class="line">726</span><br><span class="line">727</span><br><span class="line">728</span><br><span class="line">729</span><br><span class="line">730</span><br><span class="line">731</span><br><span class="line">732</span><br><span class="line">733</span><br><span class="line">734</span><br><span class="line">735</span><br><span class="line">736</span><br><span class="line">737</span><br><span class="line">738</span><br><span class="line">739</span><br><span class="line">740</span><br><span class="line">741</span><br><span class="line">742</span><br><span class="line">743</span><br><span class="line">744</span><br><span class="line">745</span><br><span class="line">746</span><br><span class="line">747</span><br><span class="line">748</span><br><span class="line">749</span><br><span class="line">750</span><br><span class="line">751</span><br><span class="line">752</span><br><span class="line">753</span><br><span class="line">754</span><br><span class="line">755</span><br><span class="line">756</span><br><span class="line">757</span><br><span class="line">758</span><br><span class="line">759</span><br><span class="line">760</span><br><span class="line">761</span><br><span class="line">762</span><br><span class="line">763</span><br><span class="line">764</span><br><span class="line">765</span><br><span class="line">766</span><br><span class="line">767</span><br><span class="line">768</span><br><span class="line">769</span><br><span class="line">770</span><br><span class="line">771</span><br><span class="line">772</span><br><span class="line">773</span><br><span class="line">774</span><br><span class="line">775</span><br><span class="line">776</span><br><span class="line">777</span><br><span class="line">778</span><br><span class="line">779</span><br><span class="line">780</span><br><span class="line">781</span><br><span class="line">782</span><br><span class="line">783</span><br><span class="line">784</span><br><span class="line">785</span><br><span class="line">786</span><br><span class="line">787</span><br><span class="line">788</span><br><span class="line">789</span><br><span class="line">790</span><br><span class="line">791</span><br><span class="line">792</span><br><span class="line">793</span><br><span class="line">794</span><br><span class="line">795</span><br><span class="line">796</span><br><span class="line">797</span><br><span class="line">798</span><br><span class="line">799</span><br><span class="line">800</span><br><span class="line">801</span><br><span class="line">802</span><br><span class="line">803</span><br><span class="line">804</span><br><span class="line">805</span><br><span class="line">806</span><br><span class="line">807</span><br><span class="line">808</span><br><span class="line">809</span><br><span class="line">810</span><br><span class="line">811</span><br><span class="line">812</span><br><span class="line">813</span><br><span class="line">814</span><br><span class="line">815</span><br><span class="line">816</span><br><span class="line">817</span><br><span class="line">818</span><br><span class="line">819</span><br><span class="line">820</span><br><span class="line">821</span><br><span class="line">822</span><br><span class="line">823</span><br><span class="line">824</span><br><span class="line">825</span><br><span class="line">826</span><br><span class="line">827</span><br><span class="line">828</span><br><span class="line">829</span><br><span class="line">830</span><br><span class="line">831</span><br><span class="line">832</span><br><span class="line">833</span><br><span class="line">834</span><br><span class="line">835</span><br><span class="line">836</span><br><span class="line">837</span><br><span class="line">838</span><br><span class="line">839</span><br><span class="line">840</span><br><span class="line">841</span><br><span class="line">842</span><br><span class="line">843</span><br><span class="line">844</span><br><span class="line">845</span><br><span class="line">846</span><br><span class="line">847</span><br><span class="line">848</span><br><span class="line">849</span><br><span class="line">850</span><br><span class="line">851</span><br><span class="line">852</span><br><span class="line">853</span><br><span class="line">854</span><br><span class="line">855</span><br><span class="line">856</span><br><span class="line">857</span><br><span class="line">858</span><br><span class="line">859</span><br><span class="line">860</span><br><span class="line">861</span><br><span class="line">862</span><br><span class="line">863</span><br><span class="line">864</span><br><span class="line">865</span><br><span class="line">866</span><br><span class="line">867</span><br><span class="line">868</span><br><span class="line">869</span><br><span class="line">870</span><br><span class="line">871</span><br><span class="line">872</span><br><span class="line">873</span><br><span class="line">874</span><br><span class="line">875</span><br><span class="line">876</span><br><span class="line">877</span><br><span class="line">878</span><br><span class="line">879</span><br><span class="line">880</span><br><span class="line">881</span><br><span class="line">882</span><br><span class="line">883</span><br><span class="line">884</span><br><span class="line">885</span><br><span class="line">886</span><br><span class="line">887</span><br><span class="line">888</span><br><span class="line">889</span><br><span class="line">890</span><br><span class="line">891</span><br><span class="line">892</span><br><span class="line">893</span><br><span class="line">894</span><br><span class="line">895</span><br><span class="line">896</span><br><span class="line">897</span><br><span class="line">898</span><br><span class="line">899</span><br><span class="line">900</span><br><span class="line">901</span><br><span class="line">902</span><br><span class="line">903</span><br><span class="line">904</span><br><span class="line">905</span><br><span class="line">906</span><br><span class="line">907</span><br><span class="line">908</span><br><span class="line">909</span><br><span class="line">910</span><br><span class="line">911</span><br><span class="line">912</span><br><span class="line">913</span><br><span class="line">914</span><br><span class="line">915</span><br><span class="line">916</span><br><span class="line">917</span><br><span class="line">918</span><br><span class="line">919</span><br><span class="line">920</span><br><span class="line">921</span><br><span class="line">922</span><br><span class="line">923</span><br><span class="line">924</span><br><span class="line">925</span><br><span class="line">926</span><br><span class="line">927</span><br><span class="line">928</span><br><span class="line">929</span><br><span class="line">930</span><br><span class="line">931</span><br><span class="line">932</span><br><span class="line">933</span><br><span class="line">934</span><br><span class="line">935</span><br><span class="line">936</span><br><span class="line">937</span><br><span class="line">938</span><br><span class="line">939</span><br><span class="line">940</span><br><span class="line">941</span><br><span class="line">942</span><br><span class="line">943</span><br><span class="line">944</span><br><span class="line">945</span><br><span class="line">946</span><br><span class="line">947</span><br><span class="line">948</span><br><span class="line">949</span><br><span class="line">950</span><br><span class="line">951</span><br><span class="line">952</span><br><span class="line">953</span><br><span class="line">954</span><br><span class="line">955</span><br><span class="line">956</span><br><span class="line">957</span><br><span class="line">958</span><br><span class="line">959</span><br><span class="line">960</span><br><span class="line">961</span><br><span class="line">962</span><br><span class="line">963</span><br><span class="line">964</span><br><span class="line">965</span><br><span class="line">966</span><br><span class="line">967</span><br><span class="line">968</span><br><span class="line">969</span><br><span class="line">970</span><br><span class="line">971</span><br><span class="line">972</span><br><span class="line">973</span><br><span class="line">974</span><br><span class="line">975</span><br><span class="line">976</span><br><span class="line">977</span><br><span class="line">978</span><br><span class="line">979</span><br><span class="line">980</span><br><span class="line">981</span><br><span class="line">982</span><br><span class="line">983</span><br><span class="line">984</span><br><span class="line">985</span><br><span class="line">986</span><br><span class="line">987</span><br><span class="line">988</span><br><span class="line">989</span><br><span class="line">990</span><br><span class="line">991</span><br><span class="line">992</span><br><span class="line">993</span><br><span class="line">994</span><br><span class="line">995</span><br><span class="line">996</span><br><span class="line">997</span><br><span class="line">998</span><br><span class="line">999</span><br><span class="line">1000</span><br><span class="line">1001</span><br><span class="line">1002</span><br><span class="line">1003</span><br><span class="line">1004</span><br><span class="line">1005</span><br><span class="line">1006</span><br><span class="line">1007</span><br><span class="line">1008</span><br><span class="line">1009</span><br><span class="line">1010</span><br><span class="line">1011</span><br><span class="line">1012</span><br><span class="line">1013</span><br><span class="line">1014</span><br><span class="line">1015</span><br><span class="line">1016</span><br><span class="line">1017</span><br><span class="line">1018</span><br><span class="line">1019</span><br><span class="line">1020</span><br><span class="line">1021</span><br><span class="line">1022</span><br><span class="line">1023</span><br><span class="line">1024</span><br><span class="line">1025</span><br><span class="line">1026</span><br><span class="line">1027</span><br><span class="line">1028</span><br><span class="line">1029</span><br><span class="line">1030</span><br><span class="line">1031</span><br><span class="line">1032</span><br><span class="line">1033</span><br><span class="line">1034</span><br><span class="line">1035</span><br><span class="line">1036</span><br><span class="line">1037</span><br><span class="line">1038</span><br><span class="line">1039</span><br><span class="line">1040</span><br><span class="line">1041</span><br><span class="line">1042</span><br><span class="line">1043</span><br><span class="line">1044</span><br><span class="line">1045</span><br><span class="line">1046</span><br><span class="line">1047</span><br><span class="line">1048</span><br><span class="line">1049</span><br><span class="line">1050</span><br><span class="line">1051</span><br><span class="line">1052</span><br><span class="line">1053</span><br><span class="line">1054</span><br><span class="line">1055</span><br><span class="line">1056</span><br><span class="line">1057</span><br><span class="line">1058</span><br><span class="line">1059</span><br><span class="line">1060</span><br><span class="line">1061</span><br><span class="line">1062</span><br><span class="line">1063</span><br><span class="line">1064</span><br><span class="line">1065</span><br><span class="line">1066</span><br><span class="line">1067</span><br><span class="line">1068</span><br><span class="line">1069</span><br><span class="line">1070</span><br><span class="line">1071</span><br><span class="line">1072</span><br><span class="line">1073</span><br><span class="line">1074</span><br><span class="line">1075</span><br><span class="line">1076</span><br><span class="line">1077</span><br><span class="line">1078</span><br><span class="line">1079</span><br><span class="line">1080</span><br><span class="line">1081</span><br><span class="line">1082</span><br><span class="line">1083</span><br><span class="line">1084</span><br><span class="line">1085</span><br><span class="line">1086</span><br><span class="line">1087</span><br><span class="line">1088</span><br><span class="line">1089</span><br><span class="line">1090</span><br><span class="line">1091</span><br><span class="line">1092</span><br><span class="line">1093</span><br><span class="line">1094</span><br><span class="line">1095</span><br><span class="line">1096</span><br><span class="line">1097</span><br><span class="line">1098</span><br><span class="line">1099</span><br><span class="line">1100</span><br><span class="line">1101</span><br><span class="line">1102</span><br><span class="line">1103</span><br><span class="line">1104</span><br><span class="line">1105</span><br><span class="line">1106</span><br><span class="line">1107</span><br><span class="line">1108</span><br><span class="line">1109</span><br><span class="line">1110</span><br><span class="line">1111</span><br><span class="line">1112</span><br><span class="line">1113</span><br><span class="line">1114</span><br><span class="line">1115</span><br><span class="line">1116</span><br><span class="line">1117</span><br><span class="line">1118</span><br><span class="line">1119</span><br><span class="line">1120</span><br><span class="line">1121</span><br><span class="line">1122</span><br><span class="line">1123</span><br><span class="line">1124</span><br><span class="line">1125</span><br><span class="line">1126</span><br><span class="line">1127</span><br><span class="line">1128</span><br><span class="line">1129</span><br><span class="line">1130</span><br><span class="line">1131</span><br><span class="line">1132</span><br><span class="line">1133</span><br><span class="line">1134</span><br><span class="line">1135</span><br><span class="line">1136</span><br><span class="line">1137</span><br><span class="line">1138</span><br><span class="line">1139</span><br><span class="line">1140</span><br><span class="line">1141</span><br><span class="line">1142</span><br><span class="line">1143</span><br><span class="line">1144</span><br><span class="line">1145</span><br><span class="line">1146</span><br><span class="line">1147</span><br><span class="line">1148</span><br><span class="line">1149</span><br><span class="line">1150</span><br><span class="line">1151</span><br><span class="line">1152</span><br><span class="line">1153</span><br><span class="line">1154</span><br><span class="line">1155</span><br><span class="line">1156</span><br><span class="line">1157</span><br><span class="line">1158</span><br><span class="line">1159</span><br><span class="line">1160</span><br><span class="line">1161</span><br><span class="line">1162</span><br><span class="line">1163</span><br><span class="line">1164</span><br><span class="line">1165</span><br><span class="line">1166</span><br><span class="line">1167</span><br><span class="line">1168</span><br><span class="line">1169</span><br><span class="line">1170</span><br><span class="line">1171</span><br><span class="line">1172</span><br><span class="line">1173</span><br><span class="line">1174</span><br><span class="line">1175</span><br><span class="line">1176</span><br><span class="line">1177</span><br><span class="line">1178</span><br><span class="line">1179</span><br><span class="line">1180</span><br><span class="line">1181</span><br><span class="line">1182</span><br><span class="line">1183</span><br><span class="line">1184</span><br><span class="line">1185</span><br><span class="line">1186</span><br><span class="line">1187</span><br><span class="line">1188</span><br><span class="line">1189</span><br><span class="line">1190</span><br><span class="line">1191</span><br><span class="line">1192</span><br><span class="line">1193</span><br><span class="line">1194</span><br><span class="line">1195</span><br><span class="line">1196</span><br><span class="line">1197</span><br><span class="line">1198</span><br><span class="line">1199</span><br><span class="line">1200</span><br><span class="line">1201</span><br><span class="line">1202</span><br><span class="line">1203</span><br><span class="line">1204</span><br><span class="line">1205</span><br><span class="line">1206</span><br><span class="line">1207</span><br><span class="line">1208</span><br><span class="line">1209</span><br><span class="line">1210</span><br><span class="line">1211</span><br><span class="line">1212</span><br><span class="line">1213</span><br><span class="line">1214</span><br><span class="line">1215</span><br><span class="line">1216</span><br><span class="line">1217</span><br><span class="line">1218</span><br><span class="line">1219</span><br><span class="line">1220</span><br><span class="line">1221</span><br><span class="line">1222</span><br><span class="line">1223</span><br><span class="line">1224</span><br><span class="line">1225</span><br><span class="line">1226</span><br><span class="line">1227</span><br><span class="line">1228</span><br><span class="line">1229</span><br><span class="line">1230</span><br><span class="line">1231</span><br><span class="line">1232</span><br><span class="line">1233</span><br><span class="line">1234</span><br><span class="line">1235</span><br><span class="line">1236</span><br><span class="line">1237</span><br><span class="line">1238</span><br><span class="line">1239</span><br><span class="line">1240</span><br><span class="line">1241</span><br></pre></td><td class="code"><pre><span class="line">/**************************************spring-cfg *********************************************/</span><br><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br><span class="line">&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;</span><br><span class="line">       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span><br><span class="line">       xmlns:context=&quot;http://www.springframework.org/schema/context&quot;</span><br><span class="line">       xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot;</span><br><span class="line">       xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;</span><br><span class="line">       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans</span><br><span class="line">    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd</span><br><span class="line">    http://www.springframework.org/schema/context</span><br><span class="line">    http://www.springframework.org/schema/context/spring-context-4.0.xsd</span><br><span class="line">    http://www.springframework.org/schema/aop</span><br><span class="line">    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd</span><br><span class="line">    http://www.springframework.org/schema/tx</span><br><span class="line">    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd&quot;&gt;</span><br><span class="line">    &lt;!--开启切面编程自动代理--&gt;</span><br><span class="line">    &lt;aop:aspectj-autoproxy proxy-target-class=&quot;true&quot;/&gt;</span><br><span class="line">    &lt;!--开启注解支持生成bean--&gt;</span><br><span class="line">    &lt;context:annotation-config/&gt;</span><br><span class="line">    &lt;!--基本包扫描路径--&gt;</span><br><span class="line">    &lt;context:component-scan base-package=&quot;com.smallkk&quot;/&gt;</span><br><span class="line">    &lt;!--整合Mybatis--&gt;</span><br><span class="line">    &lt;bean id=&quot;sqlSessionFactory&quot; class=&quot;org.mybatis.spring.SqlSessionFactoryBean&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;mapperLocations&quot; value=&quot;classpath:com/smallkk/**/*.xml&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!-- Mapper的接口扫描--&gt;</span><br><span class="line">    &lt;bean class=&quot;org.mybatis.spring.mapper.MapperScannerConfigurer&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;basePackage&quot; value=&quot;com.smallkk.*.dao&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;sqlSessionFactoryBeanName&quot; value=&quot;sqlSessionFactory&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--事务声明--&gt;</span><br><span class="line">    &lt;tx:annotation-driven transaction-manager=&quot;transactionManager&quot;/&gt;</span><br><span class="line">    &lt;bean id=&quot;transactionManager&quot; class=&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--数据源--&gt;</span><br><span class="line">    &lt;context:property-placeholder location=&quot;classpath:jdbc.properties&quot;/&gt;</span><br><span class="line">    &lt;bean id=&quot;dataSource&quot; class=&quot;com.alibaba.druid.pool.DruidDataSource&quot; destroy-method=&quot;close&quot; init-method=&quot;init&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;url&quot; value=&quot;$&#123;jdbc_url&#125;&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;username&quot; value=&quot;$&#123;jdbc_username&#125;&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;password&quot; value=&quot;$&#123;jdbc_password&#125;&quot;/&gt;</span><br><span class="line">        &lt;!-- 初始化连接大小 --&gt;</span><br><span class="line">        &lt;property name=&quot;initialSize&quot; value=&quot;0&quot;/&gt;</span><br><span class="line">        &lt;!-- 连接池最大使用连接数量 --&gt;</span><br><span class="line">        &lt;property name=&quot;maxActive&quot; value=&quot;20&quot;/&gt;</span><br><span class="line">        &lt;!-- 连接池最小空闲 --&gt;</span><br><span class="line">        &lt;property name=&quot;minIdle&quot; value=&quot;0&quot;/&gt;</span><br><span class="line">        &lt;!-- 获取连接最大等待时间 --&gt;</span><br><span class="line">        &lt;property name=&quot;maxWait&quot; value=&quot;60000&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;validationQuery&quot; value=&quot;$&#123;validationQuery&#125;&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;testOnBorrow&quot; value=&quot;false&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;testOnReturn&quot; value=&quot;false&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;testWhileIdle&quot; value=&quot;true&quot;/&gt;</span><br><span class="line">        &lt;!-- 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒 --&gt;</span><br><span class="line">        &lt;property name=&quot;timeBetweenEvictionRunsMillis&quot; value=&quot;60000&quot;/&gt;</span><br><span class="line">        &lt;!-- 配置一个连接在池中最小生存的时间，单位是毫秒 --&gt;</span><br><span class="line">        &lt;property name=&quot;minEvictableIdleTimeMillis&quot; value=&quot;25200000&quot;/&gt;</span><br><span class="line">        &lt;!-- 打开removeAbandoned功能 --&gt;</span><br><span class="line">        &lt;property name=&quot;removeAbandoned&quot; value=&quot;true&quot;/&gt;</span><br><span class="line">        &lt;!-- 1800秒，也就是30分钟 --&gt;</span><br><span class="line">        &lt;property name=&quot;removeAbandonedTimeout&quot; value=&quot;1800&quot;/&gt;</span><br><span class="line">        &lt;!-- 关闭abanded连接时输出错误日志 --&gt;</span><br><span class="line">        &lt;property name=&quot;logAbandoned&quot; value=&quot;true&quot;/&gt;</span><br><span class="line">        &lt;!-- 监控数据库 --&gt;</span><br><span class="line">        &lt;!-- &lt;property name=&quot;filters&quot; value=&quot;stat&quot; /&gt; --&gt;</span><br><span class="line">        &lt;property name=&quot;filters&quot; value=&quot;mergeStat&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--流程引擎配置项--&gt;</span><br><span class="line">    &lt;bean id=&quot;processEngineConfiguration&quot; class=&quot;org.activiti.spring.SpringProcessEngineConfiguration&quot;&gt;</span><br><span class="line">        &lt;!--数据源 和系统同一个--&gt;</span><br><span class="line">        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;transactionManager&quot; ref=&quot;transactionManager&quot;/&gt;</span><br><span class="line">        &lt;!--更新数据库表 如果没有 则创建--&gt;</span><br><span class="line">        &lt;property name=&quot;databaseSchemaUpdate&quot; value=&quot;true&quot;/&gt;</span><br><span class="line">        &lt;!-- 是否启动jobExecutor --&gt;</span><br><span class="line">        &lt;property name=&quot;jobExecutorActivate&quot; value=&quot;false&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;activityFontName&quot; value=&quot;宋体&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;labelFontName&quot; value=&quot;宋体&quot;/&gt;</span><br><span class="line">        &lt;!--自动部署流程--&gt;</span><br><span class="line">        &lt;property name=&quot;deploymentResources&quot;&gt;</span><br><span class="line">            &lt;list&gt;</span><br><span class="line">                &lt;value&gt;classpath*:workflow/re_attend.bpmn&lt;/value&gt;</span><br><span class="line">            &lt;/list&gt;</span><br><span class="line">        &lt;/property&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--流程引擎--&gt;</span><br><span class="line">    &lt;bean id=&quot;processEngine&quot; class=&quot;org.activiti.spring.ProcessEngineFactoryBean&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;processEngineConfiguration&quot; ref=&quot;processEngineConfiguration&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!-- 工作流数据存储服务 --&gt;</span><br><span class="line">    &lt;bean id=&quot;repositoryService&quot; factory-bean=&quot;processEngine&quot; factory-method=&quot;getRepositoryService&quot;/&gt;</span><br><span class="line">    &lt;!-- 工作流运行时服务 --&gt;</span><br><span class="line">    &lt;bean id=&quot;runtimeService&quot; factory-bean=&quot;processEngine&quot; factory-method=&quot;getRuntimeService&quot;/&gt;</span><br><span class="line">    &lt;!--  工作流任务服务--&gt;</span><br><span class="line">    &lt;bean id=&quot;taskService&quot; factory-bean=&quot;processEngine&quot; factory-method=&quot;getTaskService&quot;/&gt;</span><br><span class="line">    &lt;!--  工作流历史数据服务--&gt;</span><br><span class="line">    &lt;bean id=&quot;historyService&quot; factory-bean=&quot;processEngine&quot; factory-method=&quot;getHistoryService&quot;/&gt;</span><br><span class="line">    &lt;!--  工作流管理服务--&gt;</span><br><span class="line">    &lt;bean id=&quot;managementService&quot; factory-bean=&quot;processEngine&quot; factory-method=&quot;getManagementService&quot;/&gt;</span><br><span class="line">    &lt;!-- 工作流身份识别服务 --&gt;</span><br><span class="line">    &lt;bean id=&quot;IdentityService&quot; factory-bean=&quot;processEngine&quot; factory-method=&quot;getIdentityService&quot;/&gt;</span><br><span class="line">    &lt;!--将Quartz导入到spring-cfg才能够启动--&gt;</span><br><span class="line">    &lt;import resource=&quot;spring-job.xml&quot;/&gt;</span><br><span class="line">    &lt;!--导入Shiro--&gt;</span><br><span class="line">    &lt;import resource=&quot;spring-shiro.xml&quot;/&gt;</span><br><span class="line">&lt;/beans&gt;</span><br><span class="line">/***************************************spring-mvc *********************************************/</span><br><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br><span class="line">&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;</span><br><span class="line">       xmlns:context=&quot;http://www.springframework.org/schema/context&quot;</span><br><span class="line">       xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;</span><br><span class="line">       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span><br><span class="line">       xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot;</span><br><span class="line">       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans</span><br><span class="line">      http://www.springframework.org/schema/beans/spring-beans-4.0.xsd</span><br><span class="line">      http://www.springframework.org/schema/context</span><br><span class="line">      http://www.springframework.org/schema/context/spring-context.xsd</span><br><span class="line">      http://www.springframework.org/schema/mvc</span><br><span class="line">      http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd</span><br><span class="line">      http://www.springframework.org/schema/aop</span><br><span class="line">      http://www.springframework.org/schema/aop/spring-aop.xsd&quot;&gt;</span><br><span class="line">    &lt;!--开启切面编程自动代理       权限注解    基于Controller 的注解  --&gt;</span><br><span class="line">    &lt;aop:aspectj-autoproxy proxy-target-class=&quot;true&quot;/&gt;</span><br><span class="line">    &lt;bean class=&quot;org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;securityManager&quot; ref=&quot;securityManager&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--                         配置json                       --&gt;</span><br><span class="line">    &lt;mvc:annotation-driven&gt;</span><br><span class="line">        &lt;mvc:message-converters&gt;</span><br><span class="line">            &lt;!--匹配到字符串就好--&gt;</span><br><span class="line">            &lt;bean class=&quot;org.springframework.http.converter.StringHttpMessageConverter&quot;/&gt;</span><br><span class="line">            &lt;!--json--&gt;</span><br><span class="line">            &lt;bean class=&quot;com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter&quot;&gt;</span><br><span class="line">                &lt;property name=&quot;supportedMediaTypes&quot;&gt;</span><br><span class="line">                    &lt;list&gt;</span><br><span class="line">                        &lt;value&gt;text/html;charset=UTF-8&lt;/value&gt;</span><br><span class="line">                        &lt;value&gt;application/json;charset=UTF-8&lt;/value&gt;</span><br><span class="line">                    &lt;/list&gt;</span><br><span class="line">                &lt;/property&gt;</span><br><span class="line">            &lt;/bean&gt;</span><br><span class="line">        &lt;/mvc:message-converters&gt;</span><br><span class="line">    &lt;/mvc:annotation-driven&gt;</span><br><span class="line">    &lt;!--包扫描--&gt;</span><br><span class="line">    &lt;context:component-scan base-package=&quot;com.smallkk.*.controller&quot;&gt;</span><br><span class="line">        &lt;!--&lt;context:component-scan base-package=&quot;com.smallkk.*.controller&quot;&gt;   上下这两个都可以：排除service或者只扫描controller--&gt;</span><br><span class="line">        &lt;!--&lt;context:exclude-filter type=&quot;annotation&quot; expression=&quot;org.springframework.stereotype.Service&quot;/&gt;--&gt;</span><br><span class="line">    &lt;/context:component-scan&gt;</span><br><span class="line">    &lt;!--开启MVC注解 主要扫描：@Controller--&gt;</span><br><span class="line">    &lt;mvc:annotation-driven/&gt;</span><br><span class="line">    &lt;!--处理静态资源--&gt;&lt;!--加载静态资源--&gt;     &lt;!--web.xml里面配置了这个default拦截的资源--&gt;</span><br><span class="line">    &lt;mvc:default-servlet-handler/&gt;</span><br><span class="line">    &lt;!--配置拦截器--&gt;</span><br><span class="line">    &lt;mvc:interceptors&gt;</span><br><span class="line">        &lt;bean class=&quot;com.smallkk.common.interceptor.SessionInterceptor&quot;&gt;</span><br><span class="line">        &lt;/bean&gt;</span><br><span class="line">    &lt;/mvc:interceptors&gt;</span><br><span class="line">    &lt;!--前端模板 此处是Velocity 视图解析器--&gt;</span><br><span class="line">    &lt;bean class=&quot;org.springframework.web.servlet.view.velocity.VelocityViewResolver&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;suffix&quot; value=&quot;.vm&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;contentType&quot; value=&quot;text/html;charset=utf-8&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;dateToolAttribute&quot; value=&quot;date&quot;/&gt; &lt;!--日期函数--&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--Velocity配置--&gt;</span><br><span class="line">    &lt;bean id=&quot;velocityConfigurer&quot; class=&quot;org.springframework.web.servlet.view.velocity.VelocityConfigurer&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;resourceLoaderPath&quot; value=&quot;/WEB-INF/views&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;velocityProperties&quot;&gt;</span><br><span class="line">            &lt;!--Velocity的子配置项--&gt;</span><br><span class="line">            &lt;props&gt;</span><br><span class="line">                &lt;prop key=&quot;input.encoding&quot;&gt;utf-8&lt;/prop&gt;</span><br><span class="line">                &lt;prop key=&quot;output.encoding&quot;&gt;utf-8&lt;/prop&gt;</span><br><span class="line">                &lt;prop key=&quot;file.resource.loader.cache&quot;&gt;false&lt;/prop&gt;</span><br><span class="line">                &lt;prop key=&quot;file.resource.loader.modificationCheckInterval&quot;&gt;1&lt;/prop&gt;</span><br><span class="line">                &lt;prop key=&quot;velocimacro.library.autoreload&quot;&gt;false&lt;/prop&gt;</span><br><span class="line">            &lt;/props&gt;</span><br><span class="line">        &lt;/property&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--配置上传下载--&gt;</span><br><span class="line">    &lt;bean id=&quot;multipartResolver&quot; class=&quot;org.springframework.web.multipart.commons.CommonsMultipartResolver&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;maxUploadSize&quot; value=&quot;50000&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;maxInMemorySize&quot; value=&quot;50000&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;defaultEncoding&quot; value=&quot;utf-8&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--错误配置--&gt;</span><br><span class="line">    &lt;bean class=&quot;org.springframework.web.servlet.handler.SimpleMappingExceptionResolver&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;exceptionMappings&quot;&gt;</span><br><span class="line">            &lt;props&gt;</span><br><span class="line">                &lt;prop key=&quot;org.apache.shiro.authz.UnauthorizedException&quot;&gt;403&lt;/prop&gt;</span><br><span class="line">            &lt;/props&gt;</span><br><span class="line">        &lt;/property&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">&lt;/beans&gt;</span><br><span class="line"></span><br><span class="line">/**************************************spring-shiro *******************************************/</span><br><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br><span class="line">&lt;beans xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span><br><span class="line">       xmlns=&quot;http://www.springframework.org/schema/beans&quot;</span><br><span class="line">       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans</span><br><span class="line">	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd&quot;&gt;</span><br><span class="line">    &lt;!-- 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录 --&gt;</span><br><span class="line">    &lt;bean id=&quot;myRealm&quot; class=&quot;com.smallkk.common.security.MyRealm&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;credentialsMatcher&quot; ref=&quot;passwordMatcher&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;bean id=&quot;passwordMatcher&quot; class=&quot;com.smallkk.common.security.CustomCredentialsMatcher&quot;/&gt;</span><br><span class="line">    &lt;!-- Shiro默认会使用Servlet容器的Session,可通过sessionMode属性来指定使用Shiro原生Session --&gt;</span><br><span class="line">    &lt;!-- 即&lt;property name=&quot;sessionMode&quot; value=&quot;native&quot;/&gt;,详细说明见官方文档 --&gt;</span><br><span class="line">    &lt;!-- 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用&apos;realms&apos;属性代替 --&gt;</span><br><span class="line">    &lt;bean id=&quot;securityManager&quot; class=&quot;org.apache.shiro.web.mgt.DefaultWebSecurityManager&quot;&gt;</span><br><span class="line">        &lt;property name=&quot;realm&quot; ref=&quot;myRealm&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!-- Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 --&gt;</span><br><span class="line">    &lt;!-- Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 --&gt;</span><br><span class="line">    &lt;bean id=&quot;shiroFilter&quot; class=&quot;org.apache.shiro.spring.web.ShiroFilterFactoryBean&quot;&gt;</span><br><span class="line">        &lt;!-- Shiro的核心安全接口,这个属性是必须的 --&gt;</span><br><span class="line">        &lt;property name=&quot;securityManager&quot; ref=&quot;securityManager&quot;/&gt;</span><br><span class="line">        &lt;!-- 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的&quot;/login.jsp&quot;页面 --&gt;</span><br><span class="line">        &lt;property name=&quot;loginUrl&quot; value=&quot;/login&quot;/&gt;</span><br><span class="line">        &lt;!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在LoginController里硬编码为main.jsp了) --&gt;</span><br><span class="line">        &lt;!-- &lt;property name=&quot;successUrl&quot; value=&quot;/system/main&quot;/&gt; --&gt;</span><br><span class="line">        &lt;!-- 用户访问未对其授权的资源时,所显示的连接 --&gt;</span><br><span class="line">        &lt;!-- 若想更明显的测试此属性可以修改它的值,如unauthor.jsp,然后用[玄玉]登录后访问/admin/listUser.jsp就看见浏览器会显示unauthor.jsp --&gt;</span><br><span class="line">        &lt;!--&lt;property name=&quot;unauthorizedUrl&quot; value=&quot;/error/403.html&quot;/&gt;--&gt;</span><br><span class="line">        &lt;!-- Shiro连接约束配置,即过滤链的定义 --&gt;</span><br><span class="line">        &lt;!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://blog.csdn.net/jadyer/article/details/12172839 --&gt;</span><br><span class="line">        &lt;!-- 下面value值的第一个&apos;/&apos;代表的路径是相对于HttpServletRequest.getContextPath()的值来的 --&gt;</span><br><span class="line">        &lt;!-- anon：它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 --&gt;</span><br><span class="line">        &lt;!-- authc：该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter --&gt;</span><br><span class="line">        &lt;property name=&quot;filterChainDefinitions&quot;&gt;</span><br><span class="line">            &lt;value&gt;</span><br><span class="line">                /static/**=anon</span><br><span class="line">                /login=anon</span><br><span class="line">                &lt;!--/user/userinfo=authc--&gt;</span><br><span class="line">                /user/userinfo=anon</span><br><span class="line">            &lt;/value&gt;</span><br><span class="line">        &lt;/property&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!-- 保证实现了Shiro内部lifecycle函数的bean执行 --&gt;</span><br><span class="line">    &lt;bean id=&quot;lifecycleBeanPostProcessor&quot; class=&quot;org.apache.shiro.spring.LifecycleBeanPostProcessor&quot;/&gt;</span><br><span class="line">&lt;/beans&gt;</span><br><span class="line">/*************************************spring-job **********************************************/</span><br><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br><span class="line">&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;</span><br><span class="line">       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span><br><span class="line">       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans</span><br><span class="line">      http://www.springframework.org/schema/beans/spring-beans-4.0.xsd&quot;&gt;</span><br><span class="line">    &lt;bean id=&quot;taskJob&quot; class=&quot;com.smallkk.common.task.AttendCheckTask&quot;/&gt;</span><br><span class="line">    &lt;!--quartz  任务调度框架--&gt;</span><br><span class="line">    &lt;bean id=&quot;jobDetail&quot; class=&quot;org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean&quot;&gt;</span><br><span class="line">        &lt;!--要执行哪一个类--&gt;</span><br><span class="line">        &lt;property name=&quot;targetObject&quot; ref=&quot;taskJob&quot;/&gt;</span><br><span class="line">        &lt;!--要执行哪一个方法--&gt;</span><br><span class="line">        &lt;property name=&quot;targetMethod&quot; value=&quot;checkAttend&quot;/&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--触发的时机和条件--&gt;</span><br><span class="line">    &lt;bean id=&quot;attendCheck&quot; class=&quot;org.springframework.scheduling.quartz.CronTriggerFactoryBean&quot;&gt;</span><br><span class="line">        &lt;!--参考上面提到执行哪个类哪个方法的bean--&gt;</span><br><span class="line">        &lt;property name=&quot;jobDetail&quot; ref=&quot;jobDetail&quot;/&gt;</span><br><span class="line">        &lt;property name=&quot;cronExpression&quot;&gt;</span><br><span class="line">            &lt;value&gt;0/30 * * * * ?&lt;/value&gt;</span><br><span class="line">        &lt;/property&gt;</span><br><span class="line">        &lt;!-- 集群支持 Quartz有一套数据库表,还有个properties配置文件     这样就能够感知集群问题;多个tomcat的情况下一个执行了quartz,另一个就看情况执行--&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">    &lt;!--调度工厂--&gt;</span><br><span class="line">    &lt;bean id=&quot;scheduler&quot; class=&quot;org.springframework.scheduling.quartz.SchedulerFactoryBean&quot;&gt;</span><br><span class="line">        &lt;!--有很多的任务trigger--&gt;</span><br><span class="line">        &lt;property name=&quot;triggers&quot;&gt;</span><br><span class="line">            &lt;list&gt;</span><br><span class="line">                &lt;ref bean=&quot;attendCheck&quot;/&gt;</span><br><span class="line">            &lt;/list&gt;</span><br><span class="line">        &lt;/property&gt;</span><br><span class="line">    &lt;/bean&gt;</span><br><span class="line">&lt;/beans&gt;</span><br><span class="line">/***************************************MyRealm ************************************************/</span><br><span class="line">package com.smallkk.common.security;</span><br><span class="line">import com.smallkk.user.entity.Permission;</span><br><span class="line">import com.smallkk.user.entity.Role;</span><br><span class="line">import com.smallkk.user.entity.User;</span><br><span class="line">import com.smallkk.user.service.UserService;</span><br><span class="line">import org.apache.shiro.SecurityUtils;</span><br><span class="line">import org.apache.shiro.authc.*;</span><br><span class="line">import org.apache.shiro.authz.AuthorizationInfo;</span><br><span class="line">import org.apache.shiro.authz.SimpleAuthorizationInfo;</span><br><span class="line">import org.apache.shiro.realm.AuthorizingRealm;</span><br><span class="line">import org.apache.shiro.subject.PrincipalCollection;</span><br><span class="line">import org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line">/**</span><br><span class="line"> * Created By  醉美柳舞之众星捧月</span><br><span class="line"> * @author song</span><br><span class="line"> * @date 2017/10/31 13:49</span><br><span class="line"> */</span><br><span class="line">public class MyRealm extends AuthorizingRealm &#123;</span><br><span class="line">    @Autowired</span><br><span class="line">    private UserService userService;</span><br><span class="line">    /**</span><br><span class="line">     * 授权</span><br><span class="line">     * @param principalCollection</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) &#123;</span><br><span class="line">        String username = (String) principalCollection.getPrimaryPrincipal();</span><br><span class="line">        User user = userService.findUserByUserName(username);</span><br><span class="line">        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();</span><br><span class="line">        for (Role role : user.getRoleList()) &#123;</span><br><span class="line">            authorizationInfo.addRole(role.getRole());</span><br><span class="line">            for (Permission permission : role.getPermissionList()) &#123;</span><br><span class="line">                authorizationInfo.addStringPermission(permission.getPermission());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return authorizationInfo;</span><br><span class="line">    &#125;</span><br><span class="line">    /**</span><br><span class="line">     * 认证 登录</span><br><span class="line">     * @param authenticationToken</span><br><span class="line">     * @return</span><br><span class="line">     * @throws AuthenticationException</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException &#123;</span><br><span class="line">        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;</span><br><span class="line">        String username = usernamePasswordToken.getUsername();</span><br><span class="line">        User user = userService.findUserByUserName(username);</span><br><span class="line">        if (user == null) &#123;</span><br><span class="line">            return null;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());</span><br><span class="line">            SecurityUtils.getSubject().getSession().setAttribute(&quot;userinfo&quot;, user);</span><br><span class="line">            return info;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/********************************** CustomCredentialsMatcher ***********************************/</span><br><span class="line">package com.smallkk.common.security;</span><br><span class="line">import com.smallkk.common.utils.MD5Utils;</span><br><span class="line">import org.apache.shiro.authc.AuthenticationInfo;</span><br><span class="line">import org.apache.shiro.authc.AuthenticationToken;</span><br><span class="line">import org.apache.shiro.authc.UsernamePasswordToken;</span><br><span class="line">import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;</span><br><span class="line">import java.io.UnsupportedEncodingException;</span><br><span class="line">import java.security.NoSuchAlgorithmException;</span><br><span class="line">/**</span><br><span class="line"> * Created By  醉美柳舞之众星捧月</span><br><span class="line"> * @author song</span><br><span class="line"> * @date 2017/10/31 13:52</span><br><span class="line"> */</span><br><span class="line">public class CustomCredentialsMatcher extends SimpleCredentialsMatcher &#123;</span><br><span class="line">    /**</span><br><span class="line">     * 自定义密码验证</span><br><span class="line">     * @param token</span><br><span class="line">     * @param info</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            UsernamePasswordToken usertoken = (UsernamePasswordToken) token;</span><br><span class="line">            String password = String.valueOf(usertoken.getPassword());</span><br><span class="line">            Object tokenCredentials = MD5Utils.encrptyPassword(password);</span><br><span class="line">            Object accountCredentials = getCredentials(info);</span><br><span class="line">            return equals(tokenCredentials, accountCredentials);</span><br><span class="line">        &#125; catch (NoSuchAlgorithmException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; catch (UnsupportedEncodingException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/***************************************LoginController***************************************/</span><br><span class="line">package com.smallkk.login.controller;</span><br><span class="line">import com.smallkk.user.entity.User;</span><br><span class="line">import com.smallkk.user.service.UserService;</span><br><span class="line">import org.apache.commons.io.FileUtils;</span><br><span class="line">import org.apache.shiro.SecurityUtils;</span><br><span class="line">import org.apache.shiro.authc.UsernamePasswordToken;</span><br><span class="line">import org.apache.shiro.subject.Subject;</span><br><span class="line">import org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line">import org.springframework.http.HttpHeaders;</span><br><span class="line">import org.springframework.http.HttpStatus;</span><br><span class="line">import org.springframework.http.MediaType;</span><br><span class="line">import org.springframework.http.ResponseEntity;</span><br><span class="line">import org.springframework.stereotype.Controller;</span><br><span class="line">import org.springframework.web.bind.annotation.RequestMapping;</span><br><span class="line">import org.springframework.web.bind.annotation.ResponseBody;</span><br><span class="line">import javax.imageio.ImageIO;</span><br><span class="line">import javax.servlet.http.HttpServletRequest;</span><br><span class="line">import javax.servlet.http.HttpServletResponse;</span><br><span class="line">import javax.servlet.http.HttpSession;</span><br><span class="line">import java.awt.*;</span><br><span class="line">import java.awt.image.BufferedImage;</span><br><span class="line">import java.io.File;</span><br><span class="line">import java.io.IOException;</span><br><span class="line">import java.io.UnsupportedEncodingException;</span><br><span class="line">import java.security.NoSuchAlgorithmException;</span><br><span class="line">import java.util.Random;</span><br><span class="line">/**</span><br><span class="line"> * Created By  醉美柳舞之众星捧月</span><br><span class="line"> * @author song</span><br><span class="line"> * @date 2017/10/19 0:03</span><br><span class="line"> */</span><br><span class="line">@Controller</span><br><span class="line">@RequestMapping(&quot;login&quot;)</span><br><span class="line">public class LoginController &#123;</span><br><span class="line">    @Autowired</span><br><span class="line">    private UserService userService;</span><br><span class="line">    /**</span><br><span class="line">     * Author song  醉美柳舞之众星捧月</span><br><span class="line">     * Date &amp; Time  2017/10/19 22:57</span><br><span class="line">     * Description  登录页面</span><br><span class="line">     */</span><br><span class="line">    @RequestMapping()</span><br><span class="line">    public String login() &#123;</span><br><span class="line">        return &quot;login&quot;;</span><br><span class="line">    &#125;</span><br><span class="line">    /**</span><br><span class="line">     * Author song  醉美柳舞之众星捧月</span><br><span class="line">     * Date &amp; Time  2017/10/19 22:24</span><br><span class="line">     * Description  账号密码校验</span><br><span class="line">     */</span><br><span class="line">    @RequestMapping(&quot;/check&quot;)</span><br><span class="line">    @ResponseBody    //  因为异步;要添加@ResponseBody注解; 否则就404</span><br><span class="line">    public String CheckLogin(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException, NoSuchAlgorithmException &#123;</span><br><span class="line">        String username = request.getParameter(&quot;username&quot;);</span><br><span class="line">        String pwd = request.getParameter(&quot;password&quot;);</span><br><span class="line">        String remember = request.getParameter(&quot;remember&quot;);</span><br><span class="line">        UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);</span><br><span class="line">        if (remember.equals(&quot;1&quot;)) &#123;</span><br><span class="line">            token.setRememberMe(true);</span><br><span class="line">        &#125;</span><br><span class="line">        Subject subject = SecurityUtils.getSubject();</span><br><span class="line">        try &#123;</span><br><span class="line">            subject.login(token);</span><br><span class="line">            SecurityUtils.getSubject().getSession().setTimeout(30000);</span><br><span class="line">        &#125; catch (Exception e) &#123;</span><br><span class="line">            return &quot;login_fail&quot;;</span><br><span class="line">        &#125;</span><br><span class="line">        return &quot;login_succ&quot;;</span><br><span class="line">//        此处调用MD5 和 数据库进行匹配  已经被   Shiro取代  !!!!</span><br><span class="line">//        User user = userService.findUserByUserName(username);</span><br><span class="line">//        if (user != null) &#123;</span><br><span class="line">//            if (MD5Utils.checkPassword(pwd, user.getPassword())) &#123;</span><br><span class="line">//                //此处登录成功 可以设置session</span><br><span class="line">//                request.getSession().setAttribute(&quot;userinfo&quot;, user);</span><br><span class="line">//                return &quot;login_succ&quot;;</span><br><span class="line">//            &#125; else &#123;</span><br><span class="line">//                //返回失败信号</span><br><span class="line">//                return &quot;login_fail&quot;;</span><br><span class="line">//            &#125;</span><br><span class="line">//        &#125; else &#123;</span><br><span class="line">//            return &quot;login_fail&quot;;</span><br><span class="line">//        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    /**</span><br><span class="line">     * Author song  醉美柳舞之众星捧月</span><br><span class="line">     * Date &amp; Time  2017/10/19 23:03</span><br><span class="line">     * Description  注册 植入用户</span><br><span class="line">     */</span><br><span class="line">    @RequestMapping(&quot;/register&quot;)</span><br><span class="line">    @ResponseBody</span><br><span class="line">    public String register(User user, HttpServletRequest request, HttpSession session) throws UnsupportedEncodingException, NoSuchAlgorithmException &#123;</span><br><span class="line">        // @RequestBody 通过json 的形式把用户传进来</span><br><span class="line">        String emailuser = request.getParameter(&quot;emailuser&quot;);</span><br><span class="line">        String emailpwd = request.getParameter(&quot;emailpwd&quot;);</span><br><span class="line">        String emailcode = request.getParameter(&quot;emailcode&quot;);</span><br><span class="line">        String strcode = session.getAttribute(&quot;strCode&quot;).toString();</span><br><span class="line">        String realname = request.getParameter(&quot;realname&quot;);</span><br><span class="line">        String mobile = request.getParameter(&quot;mobile&quot;);</span><br><span class="line">        if (emailcode.equals(strcode)) &#123;</span><br><span class="line">            user.setUsername(emailuser);</span><br><span class="line">            user.setPassword(emailpwd);</span><br><span class="line">            user.setRealName(realname);</span><br><span class="line">            user.setMobile(mobile);</span><br><span class="line">            userService.createUser(user);</span><br><span class="line">            return &quot;register_succ&quot;;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            return &quot;register_error&quot;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    //    文件下载</span><br><span class="line">    @RequestMapping(&quot;/down&quot;)</span><br><span class="line">    public ResponseEntity&lt;byte[]&gt; download() throws IOException &#123;</span><br><span class="line">        String path = &quot;&quot;;</span><br><span class="line">        //  文件所在地和文件的名字格式</span><br><span class="line">        File file = new File(path);</span><br><span class="line">        HttpHeaders headers = new HttpHeaders();      //告诉Http头</span><br><span class="line">        headers.setContentDispositionFormData(&quot;attachment&quot;, path);   //我这儿有一个附件  ;path是路径</span><br><span class="line">        headers.setContentType(MediaType.IMAGE_JPEG);     // 这个附件是什么类型的</span><br><span class="line">        return new ResponseEntity&lt;byte[]&gt;(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);</span><br><span class="line">    &#125;</span><br><span class="line">    @RequestMapping(&quot;/authCode&quot;)</span><br><span class="line">    public void getAuthCode(HttpServletRequest request, HttpServletResponse response, HttpSession session)</span><br><span class="line">            throws IOException &#123;</span><br><span class="line">        int width = 63;</span><br><span class="line">        int height = 37;</span><br><span class="line">        Random random = new Random();</span><br><span class="line">        //设置response头信息</span><br><span class="line">        //禁止缓存</span><br><span class="line">        response.setHeader(&quot;Pragma&quot;, &quot;No-cache&quot;);</span><br><span class="line">        response.setHeader(&quot;Cache-Control&quot;, &quot;no-cache&quot;);</span><br><span class="line">        response.setDateHeader(&quot;Expires&quot;, 0);</span><br><span class="line">        //生成缓冲区image类</span><br><span class="line">        BufferedImage image = new BufferedImage(width, height, 1);</span><br><span class="line">        //产生image类的Graphics用于绘制操作</span><br><span class="line">        Graphics g = image.getGraphics();</span><br><span class="line">        //Graphics类的样式</span><br><span class="line">        g.setColor(this.getRandColor(200, 250));</span><br><span class="line">        g.setFont(new Font(&quot;Times New Roman&quot;, 0, 28));</span><br><span class="line">        g.fillRect(0, 0, width, height);</span><br><span class="line">        //绘制干扰线</span><br><span class="line">        for (int i = 0; i &lt; 40; i++) &#123;</span><br><span class="line">            g.setColor(this.getRandColor(130, 200));</span><br><span class="line">            int x = random.nextInt(width);</span><br><span class="line">            int y = random.nextInt(height);</span><br><span class="line">            int x1 = random.nextInt(12);</span><br><span class="line">            int y1 = random.nextInt(12);</span><br><span class="line">            g.drawLine(x, y, x + x1, y + y1);</span><br><span class="line">        &#125;</span><br><span class="line">        //绘制字符</span><br><span class="line">        String strCode = &quot;&quot;;</span><br><span class="line">        for (int i = 0; i &lt; 4; i++) &#123;</span><br><span class="line">            String rand = String.valueOf(random.nextInt(10));</span><br><span class="line">            strCode = strCode + rand;</span><br><span class="line">            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));</span><br><span class="line">            g.drawString(rand, 13 * i + 6, 28);</span><br><span class="line">        &#125;</span><br><span class="line">        //将字符保存到session中用于前端的验证</span><br><span class="line">        session.setAttribute(&quot;strCode&quot;, strCode);</span><br><span class="line">        g.dispose();</span><br><span class="line">        ImageIO.write(image, &quot;JPEG&quot;, response.getOutputStream());</span><br><span class="line">        response.getOutputStream().flush();</span><br><span class="line">    &#125;</span><br><span class="line">    //创建颜色</span><br><span class="line">    Color getRandColor(int fc, int bc) &#123;</span><br><span class="line">        Random random = new Random();</span><br><span class="line">        if (fc &gt; 255) &#123;</span><br><span class="line">            fc = 255;</span><br><span class="line">        &#125;</span><br><span class="line">        if (bc &gt; 255) &#123;</span><br><span class="line">            bc = 255;</span><br><span class="line">        &#125;</span><br><span class="line">        int r = fc + random.nextInt(bc - fc);</span><br><span class="line">        int g = fc + random.nextInt(bc - fc);</span><br><span class="line">        int b = fc + random.nextInt(bc - fc);</span><br><span class="line">        return new Color(r, g, b);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/**************************************** MD5 *********************************************/</span><br><span class="line">package com.smallkk.common.utils;</span><br><span class="line">import java.io.UnsupportedEncodingException;</span><br><span class="line">import java.security.MessageDigest;</span><br><span class="line">import java.security.NoSuchAlgorithmException;</span><br><span class="line">import java.util.Base64;</span><br><span class="line">/**</span><br><span class="line"> * Created By  醉美柳舞之众星捧月</span><br><span class="line"> * @author song</span><br><span class="line"> * @date 2017/10/19 22:09</span><br><span class="line"> */</span><br><span class="line">public class MD5Utils &#123;</span><br><span class="line">    /**</span><br><span class="line">     * Author song  醉美柳舞之众星捧月</span><br><span class="line">     * Date &amp; Time  2017/10/19 22:09</span><br><span class="line">     * Description  消息摘要算法 人类看不懂的</span><br><span class="line">     */</span><br><span class="line">    public static String encrptyPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException &#123;</span><br><span class="line">        MessageDigest md5 = MessageDigest.getInstance(&quot;MD5&quot;);</span><br><span class="line">//JDK9.0已经不再支持BASE64Encoder;rj.jar包已经从JDK里面移除;</span><br><span class="line">        //用import java.util.Base64;来替换</span><br><span class="line">//        BASE64Encoder base64Encoder = new BASE64Encoder();</span><br><span class="line">//        String result = base64Encoder.encode(md5.digest(password.getBytes(&quot;utf-8&quot;)));</span><br><span class="line">        Base64.Encoder encoder = Base64.getEncoder();</span><br><span class="line">        String result = encoder.encodeToString(md5.digest(password.getBytes(&quot;utf-8&quot;)));</span><br><span class="line">        // 通过utf-8的方式生成字节码 把密码转换 ;再让BASE64Encoder转换一下,防止加密乱码啊什么的</span><br><span class="line">        return result;</span><br><span class="line">    &#125;</span><br><span class="line">    /**</span><br><span class="line">     * Author song  醉美柳舞之众星捧月</span><br><span class="line">     * Date &amp; Time  2017/10/19 22:17</span><br><span class="line">     * Description   判断传入的密码和数据库中的密码是否一致</span><br><span class="line">     */</span><br><span class="line">    public static boolean checkPassword(String inputPwd, String dbPwd) throws UnsupportedEncodingException, NoSuchAlgorithmException &#123;</span><br><span class="line">        //分别传入的是  用户输入传入的密码,数据库中已有的传入的密码</span><br><span class="line">        String result = encrptyPassword(inputPwd);</span><br><span class="line">        if (result.equals(dbPwd)) &#123;</span><br><span class="line">            return true;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            return false;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/************************************* foreach*********************************************/</span><br><span class="line">@RequestMapping</span><br><span class="line">public String toReAttend(Model model, HttpSession session) &#123;</span><br><span class="line">    User user = (User) SecurityUtils.getSubject().getSession().getAttribute(&quot;userinfo&quot;);</span><br><span class="line">    List&lt;ReAttend&gt; reAttendList = reAttendService.listReAttend(user.getUsername());</span><br><span class="line">    model.addAttribute(&quot;reAttendList&quot;, reAttendList);</span><br><span class="line">    return &quot;reAttend&quot;;</span><br><span class="line">&#125;</span><br><span class="line">&lt;table id=&quot;attend_grid&quot; class=&quot;table table-bordered table-striped&quot;&gt;</span><br><span class="line">    &lt;thead&gt;</span><br><span class="line">    &lt;tr&gt;</span><br><span class="line">        &lt;th&gt;序号&lt;/th&gt;</span><br><span class="line">        &lt;th&gt;补签日期&lt;/th&gt;</span><br><span class="line">        &lt;th&gt;早打卡&lt;/th&gt;</span><br><span class="line">        &lt;th&gt;晚打卡&lt;/th&gt;</span><br><span class="line">        &lt;th&gt;当前处理人&lt;/th&gt;</span><br><span class="line">        &lt;th&gt;状态&lt;/th&gt;</span><br><span class="line">    &lt;/tr&gt;</span><br><span class="line">    &lt;/thead&gt;</span><br><span class="line">    #foreach( $reAttend in $reAttendList )</span><br><span class="line">        &lt;tr&gt;</span><br><span class="line">            &lt;td&gt;$foreach.count&lt;/td&gt;</span><br><span class="line">            &lt;td&gt;$date.format(&apos;yyyy-MM-dd&apos;,$reAttend.attendDate)&lt;/td&gt;</span><br><span class="line">            &lt;td&gt;$date.format(&apos;HH:mm:ss&apos;,$reAttend.reAttendMor)&lt;/td&gt;</span><br><span class="line">            &lt;td&gt;$date.format(&apos;HH:mm:ss&apos;,$reAttend.reAttendEve)&lt;/td&gt;</span><br><span class="line">            &lt;td&gt;$reAttend.currentHandler&lt;/td&gt;</span><br><span class="line">            &lt;td&gt;</span><br><span class="line">                #if($reAttend.status==1)</span><br><span class="line">                    处理中</span><br><span class="line">                #elseif($reAttend.status==2)</span><br><span class="line">                    审批通过</span><br><span class="line">                #elseif($reAttend.status==3)</span><br><span class="line">                    审批不通过</span><br><span class="line">                #end</span><br><span class="line">            &lt;/td&gt;</span><br><span class="line">        &lt;/tr&gt;</span><br><span class="line">    #end</span><br><span class="line">&lt;/table&gt;</span><br><span class="line">/**************************************多表联合*********************************************/</span><br><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;</span><br><span class="line">&lt;!DOCTYPE mapper PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot; &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot; &gt;</span><br><span class="line">&lt;mapper namespace=&quot;com.smallkk.user.dao.UserMapper&quot;&gt;</span><br><span class="line">    &lt;resultMap id=&quot;BaseResultMap&quot; type=&quot;com.smallkk.user.entity.User&quot;&gt;</span><br><span class="line">        &lt;id column=&quot;id&quot; property=&quot;id&quot; jdbcType=&quot;BIGINT&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;username&quot; property=&quot;username&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;password&quot; property=&quot;password&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;mobile&quot; property=&quot;mobile&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;real_name&quot; property=&quot;realName&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;head_image&quot; property=&quot;headImage&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">        &lt;collection property=&quot;roleList&quot; resultMap=&quot;RoleResultMap&quot;/&gt;</span><br><span class="line">    &lt;/resultMap&gt;</span><br><span class="line">    &lt;resultMap id=&quot;RoleResultMap&quot; type=&quot;com.smallkk.user.entity.Role&quot;&gt;</span><br><span class="line">        &lt;id column=&quot;roleid&quot; property=&quot;roleid&quot; jdbcType=&quot;BIGINT&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;role&quot; property=&quot;role&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;rd&quot; property=&quot;description&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">        &lt;collection property=&quot;permissionList&quot; resultMap=&quot;PermissionResultMap&quot;/&gt;</span><br><span class="line">    &lt;/resultMap&gt;</span><br><span class="line">    &lt;resultMap id=&quot;PermissionResultMap&quot; type=&quot;com.smallkk.user.entity.Permission&quot;&gt;</span><br><span class="line">        &lt;id column=&quot;permissionid&quot; property=&quot;permissionid&quot; jdbcType=&quot;BIGINT&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;permission&quot; property=&quot;permission&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">        &lt;result column=&quot;pd&quot; property=&quot;description&quot; jdbcType=&quot;VARCHAR&quot;/&gt;</span><br><span class="line">    &lt;/resultMap&gt;</span><br><span class="line">    &lt;sql id=&quot;Permission_Column_List&quot;&gt;    permissionid, permission, p.description pd&lt;/sql&gt;</span><br><span class="line">    &lt;sql id=&quot;Role_Column_List&quot;&gt;    roleid, role, r.description rd&lt;/sql&gt;</span><br><span class="line">    &lt;sql id=&quot;Base_Column_List&quot;&gt;    id, username, password, mobile, real_name, head_image&lt;/sql&gt;</span><br><span class="line">    &lt;select id=&quot;selectByPrimaryKey&quot; resultMap=&quot;BaseResultMap&quot; parameterType=&quot;java.lang.Long&quot;&gt;select</span><br><span class="line">        &lt;include refid=&quot;Base_Column_List&quot;/&gt;</span><br><span class="line">        from user where id = #&#123;id,jdbcType=BIGINT&#125;</span><br><span class="line">    &lt;/select&gt;</span><br><span class="line">    &lt;select id=&quot;selectByUserName&quot; resultMap=&quot;BaseResultMap&quot;&gt;SELECT&lt;include refid=&quot;Base_Column_List&quot;/&gt;,&lt;include</span><br><span class="line">            refid=&quot;Role_Column_List&quot;/&gt;,</span><br><span class="line">        &lt;include refid=&quot;Permission_Column_List&quot;/&gt;</span><br><span class="line">        FROM user u LEFT JOIN user_role ur ON ur.user_id = u.id LEFT JOIN role r ON ur.role_id=r.roleid LEFT JOIN</span><br><span class="line">        role_permission rp ON rp.role_id=r.roleid LEFT JOIN permission p ON p.permissionid = rp.permission_id WHERE</span><br><span class="line">        username = #&#123;username&#125;</span><br><span class="line">    &lt;/select&gt;</span><br><span class="line">    &lt;delete id=&quot;deleteByPrimaryKey&quot; parameterType=&quot;java.lang.Long&quot;&gt;delete from userwhere id = #&#123;id,jdbcType=BIGINT&#125;</span><br><span class="line">&lt;/delete&gt;</span><br><span class="line">    &lt;insert id=&quot;insert&quot; parameterType=&quot;com.smallkk.user.entity.User&quot;&gt;insert into user (id, username, password,   mobile, real_name, head_image  )values (#&#123;id,jdbcType=BIGINT&#125;, #&#123;username,jdbcType=VARCHAR&#125;, #&#123;password,jdbcType=VARCHAR&#125;,   #&#123;mobile,jdbcType=VARCHAR&#125;, #&#123;realName,jdbcType=VARCHAR&#125;, #&#123;headImage,jdbcType=VARCHAR&#125;  )</span><br><span class="line">  &lt;/insert&gt;</span><br><span class="line">    &lt;insert id=&quot;insertSelective&quot; parameterType=&quot;com.smallkk.user.entity.User&quot;&gt;insert into user</span><br><span class="line">        &lt;trim prefix=&quot;(&quot; suffix=&quot;)&quot; suffixOverrides=&quot;,&quot;&gt;</span><br><span class="line">            &lt;if test=&quot;id != null&quot;&gt;id,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;username != null&quot;&gt;username,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;password != null&quot;&gt;password,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;mobile != null&quot;&gt;mobile,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;realName != null&quot;&gt;real_name,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;headImage != null&quot;&gt;head_image,&lt;/if&gt;</span><br><span class="line">        &lt;/trim&gt;</span><br><span class="line">        &lt;trim prefix=&quot;values (&quot; suffix=&quot;)&quot; suffixOverrides=&quot;,&quot;&gt;</span><br><span class="line">            &lt;if test=&quot;id != null&quot;&gt;#&#123;id,jdbcType=BIGINT&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;username != null&quot;&gt;#&#123;username,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;password != null&quot;&gt;#&#123;password,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;mobile != null&quot;&gt;#&#123;mobile,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;realName != null&quot;&gt;#&#123;realName,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;headImage != null&quot;&gt;#&#123;headImage,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">        &lt;/trim&gt;</span><br><span class="line">    &lt;/insert&gt;</span><br><span class="line">    &lt;update id=&quot;updateByPrimaryKeySelective&quot; parameterType=&quot;com.smallkk.user.entity.User&quot;&gt;update user</span><br><span class="line">        &lt;set&gt;</span><br><span class="line">            &lt;if test=&quot;username != null&quot;&gt;username = #&#123;username,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;password != null&quot;&gt;password = #&#123;password,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;mobile != null&quot;&gt;mobile = #&#123;mobile,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;realName != null&quot;&gt;real_name = #&#123;realName,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">            &lt;if test=&quot;headImage != null&quot;&gt;head_image = #&#123;headImage,jdbcType=VARCHAR&#125;,&lt;/if&gt;</span><br><span class="line">        &lt;/set&gt;</span><br><span class="line">        where id = #&#123;id,jdbcType=BIGINT&#125;</span><br><span class="line">    &lt;/update&gt;</span><br><span class="line">    &lt;update id=&quot;updateByPrimaryKey&quot; parameterType=&quot;com.smallkk.user.entity.User&quot;&gt;update userset username = #&#123;username,jdbcType=VARCHAR&#125;,  password = #&#123;password,jdbcType=VARCHAR&#125;,  mobile = #&#123;mobile,jdbcType=VARCHAR&#125;,  real_name = #&#123;realName,jdbcType=VARCHAR&#125;,  head_image = #&#123;headImage,jdbcType=VARCHAR&#125;where id = #&#123;id,jdbcType=BIGINT&#125;</span><br><span class="line">  &lt;/update&gt;</span><br><span class="line">&lt;/mapper&gt;</span><br><span class="line">/*************************************** Ajax① *******************************************/</span><br><span class="line">$(function () &#123;</span><br><span class="line">    $.ajax(&#123;</span><br><span class="line">        type: &quot;POST&quot;,</span><br><span class="line">        url: &quot;/user/userinfo&quot;,</span><br><span class="line">        dataType: &quot;json&quot;,</span><br><span class="line">        contentType: &quot;application/json;charset=UTF-8&quot;,</span><br><span class="line">        data: &#123;&#125;,</span><br><span class="line">        success: function (data) &#123;</span><br><span class="line">            $(&quot;.user_head_img&quot;).attr(&quot;src&quot;, data.headImage);</span><br><span class="line">            $(&quot;.user_name&quot;).html(data.realName);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br><span class="line">/*************************************** Ajax② *********************************************/</span><br><span class="line">var user = $(&quot;#user&quot;).val();</span><br><span class="line">var pwd = $(&quot;#pwd&quot;).val();</span><br><span class="line">$.ajax(&#123;</span><br><span class="line">    type: &quot;POST&quot;,</span><br><span class="line">    url: &quot;/login/check&quot;,</span><br><span class="line">    data: &#123;&quot;username&quot;: user, &quot;password&quot;: pwd &#125;,</span><br><span class="line">    success: function (data) &#123;</span><br><span class="line">        if (data == &quot;login_succ&quot;) &#123;</span><br><span class="line">            window.location.href = &quot;/user/home&quot;;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            $.pnotify(&#123;</span><br><span class="line">                title: &apos;Error !&apos;,</span><br><span class="line">                text: &apos;用户名或密码不正确,请重新输入.&apos;,</span><br><span class="line">                type: &apos;success&apos;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line">String username = request.getParameter(&quot;username&quot;);</span><br><span class="line">String pwd = request.getParameter(&quot;password&quot;);</span><br><span class="line">/************************************ SessionInterceptor ****************************************/</span><br><span class="line">package com.smallkk.common.interceptor;</span><br><span class="line">import com.smallkk.user.entity.User;</span><br><span class="line">import org.springframework.web.servlet.HandlerInterceptor;</span><br><span class="line">import org.springframework.web.servlet.ModelAndView;</span><br><span class="line">import javax.servlet.http.HttpServletRequest;</span><br><span class="line">import javax.servlet.http.HttpServletResponse;</span><br><span class="line">import javax.servlet.http.HttpSession;</span><br><span class="line">/**</span><br><span class="line"> * Created By  醉美柳舞之众星捧月</span><br><span class="line"> * @author song</span><br><span class="line"> * @date 2017/10/19 23:55</span><br><span class="line"> */</span><br><span class="line">public class SessionInterceptor implements HandlerInterceptor &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception &#123;</span><br><span class="line">        String uri = request.getRequestURI();</span><br><span class="line">        if (uri.indexOf(&quot;login&quot;) &gt;= 0 || uri.indexOf(&quot;sign&quot;) &gt;= 0 || uri.indexOf(&quot;error&quot;) &gt;= 0||uri.indexOf(&quot;reAttend&quot;) &gt;= 0) &#123;</span><br><span class="line">            return true;</span><br><span class="line">        &#125;</span><br><span class="line">        HttpSession session = request.getSession();</span><br><span class="line">        User user = (User) session.getAttribute(&quot;userinfo&quot;);</span><br><span class="line">        if (user != null) &#123;</span><br><span class="line">            return true;</span><br><span class="line">        &#125;</span><br><span class="line">        // 转发到登录页面</span><br><span class="line">        request.getRequestDispatcher(&quot;/login&quot;).forward(request, response);</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">/**************************************** web.xml *********************************************/</span><br><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br><span class="line">&lt;web-app xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span><br><span class="line">         xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;</span><br><span class="line">         xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;</span><br><span class="line">         version=&quot;3.0&quot;&gt;</span><br><span class="line">    &lt;display-name&gt;work-attendance&lt;/display-name&gt;</span><br><span class="line">    &lt;!--spring配置的位置--&gt;</span><br><span class="line">    &lt;context-param&gt;</span><br><span class="line">        &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;</span><br><span class="line">        &lt;param-value&gt;classpath:spring-cfg.xml&lt;/param-value&gt;</span><br><span class="line">    &lt;/context-param&gt;</span><br><span class="line">    &lt;!--spring在监听器中启动--&gt;</span><br><span class="line">    &lt;listener&gt;</span><br><span class="line">        &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;</span><br><span class="line">    &lt;/listener&gt;</span><br><span class="line">    &lt;!--添加Spring乱码过滤器--&gt;</span><br><span class="line">    &lt;filter&gt;</span><br><span class="line">        &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;</span><br><span class="line">        &lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter&lt;/filter-class&gt;</span><br><span class="line">        &lt;async-supported&gt;true&lt;/async-supported&gt;</span><br><span class="line">        &lt;init-param&gt;</span><br><span class="line">            &lt;param-name&gt;encoding&lt;/param-name&gt;</span><br><span class="line">            &lt;param-value&gt;UTF-8&lt;/param-value&gt;</span><br><span class="line">        &lt;/init-param&gt;</span><br><span class="line">    &lt;/filter&gt;</span><br><span class="line">    &lt;filter-mapping&gt;</span><br><span class="line">        &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;</span><br><span class="line">        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;</span><br><span class="line">    &lt;/filter-mapping&gt;</span><br><span class="line">    &lt;!--shiro权限过滤器--&gt;</span><br><span class="line">    &lt;filter&gt;</span><br><span class="line">        &lt;filter-name&gt;shiroFilter&lt;/filter-name&gt;</span><br><span class="line">        &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;</span><br><span class="line">        &lt;async-supported&gt;true&lt;/async-supported&gt;</span><br><span class="line">        &lt;init-param&gt;</span><br><span class="line">            &lt;!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 --&gt;</span><br><span class="line">            &lt;param-name&gt;targetFilterLifecycle&lt;/param-name&gt;</span><br><span class="line">            &lt;param-value&gt;true&lt;/param-value&gt;</span><br><span class="line">        &lt;/init-param&gt;</span><br><span class="line">    &lt;/filter&gt;</span><br><span class="line">    &lt;filter-mapping&gt;</span><br><span class="line">        &lt;filter-name&gt;shiroFilter&lt;/filter-name&gt;</span><br><span class="line">        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;</span><br><span class="line">    &lt;/filter-mapping&gt;</span><br><span class="line">    &lt;!--Spring MVC servlet--&gt;</span><br><span class="line">    &lt;servlet&gt;</span><br><span class="line">        &lt;servlet-name&gt;SpringMVC&lt;/servlet-name&gt;</span><br><span class="line">        &lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&lt;/servlet-class&gt;</span><br><span class="line">        &lt;init-param&gt;</span><br><span class="line">            &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;</span><br><span class="line">            &lt;param-value&gt;classpath:spring-mvc.xml&lt;/param-value&gt;</span><br><span class="line">        &lt;/init-param&gt;</span><br><span class="line">        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;</span><br><span class="line">        &lt;async-supported&gt;true&lt;/async-supported&gt;</span><br><span class="line">    &lt;/servlet&gt;</span><br><span class="line">    &lt;servlet-mapping&gt;</span><br><span class="line">        &lt;servlet-name&gt;SpringMVC&lt;/servlet-name&gt;</span><br><span class="line">        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;</span><br><span class="line">    &lt;/servlet-mapping&gt;</span><br><span class="line">    &lt;!--此处的default是spring-mvc里面的default处理默认静态资源的;此处的作用是将路径中的资源进行放过--&gt;</span><br><span class="line">    &lt;!--那么这个路径中的资源就不会经过interceptor这个拦截器了--&gt;</span><br><span class="line">    &lt;servlet-mapping&gt;</span><br><span class="line">        &lt;servlet-name&gt;default&lt;/servlet-name&gt;</span><br><span class="line">        &lt;url-pattern&gt;/static/*&lt;/url-pattern&gt;</span><br><span class="line">    &lt;/servlet-mapping&gt;</span><br><span class="line">    &lt;error-page&gt;</span><br><span class="line">        &lt;error-code&gt;404&lt;/error-code&gt;</span><br><span class="line">        &lt;location&gt;/WEB-INF/views/error/404.html&lt;/location&gt;</span><br><span class="line">    &lt;/error-page&gt;</span><br><span class="line">    &lt;error-page&gt;</span><br><span class="line">        &lt;error-code&gt;500&lt;/error-code&gt;</span><br><span class="line">        &lt;location&gt;/WEB-INF/views/error/500.html&lt;/location&gt;</span><br><span class="line">    &lt;/error-page&gt;</span><br><span class="line">    &lt;!--配置log4j--&gt;</span><br><span class="line">    &lt;context-param&gt;</span><br><span class="line">        &lt;param-name&gt;log4jConfigLocation&lt;/param-name&gt;</span><br><span class="line">        &lt;param-value&gt;classpath:log4j.properties&lt;/param-value&gt;</span><br><span class="line">    &lt;/context-param&gt;</span><br><span class="line">&lt;/web-app&gt;</span><br><span class="line"> /**************************************** pom.xml *******************************************/       </span><br><span class="line">&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span><br><span class="line">         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;</span><br><span class="line">    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;</span><br><span class="line">    &lt;groupId&gt;com&lt;/groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;smallkk&lt;/artifactId&gt;</span><br><span class="line">    &lt;packaging&gt;war&lt;/packaging&gt;</span><br><span class="line">    &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;</span><br><span class="line">    &lt;name&gt;work-attendance&lt;/name&gt;</span><br><span class="line">    &lt;properties&gt;</span><br><span class="line">        &lt;spring.version&gt;4.1.4.RELEASE&lt;/spring.version&gt;</span><br><span class="line">        &lt;mybatis.version&gt;3.2.8&lt;/mybatis.version&gt;</span><br><span class="line">        &lt;slf4j.version&gt;1.7.7&lt;/slf4j.version&gt;</span><br><span class="line">        &lt;log4j.version&gt;1.2.17&lt;/log4j.version&gt;</span><br><span class="line">        &lt;quartz.version&gt;2.2.2&lt;/quartz.version&gt;</span><br><span class="line">        &lt;activiti.version&gt;5.15.1&lt;/activiti.version&gt;</span><br><span class="line">    &lt;/properties&gt;</span><br><span class="line">    &lt;dependencies&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-core&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-context&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 添加spring-tx包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-tx&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 添加spring-jdbc包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 为了方便进行单元测试，添加spring-test包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-test&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!--添加spring-web包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-web&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-webmvc&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-aop&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-context-support&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;spring.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!--添加aspectjweaver包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.aspectj&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;aspectjweaver&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.8.5&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 添加mybatis的核心包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.mybatis&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;mybatis&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;mybatis.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 添加mybatis与Spring整合的核心包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.mybatis&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;mybatis-spring&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.2.2&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 添加servlet3.0核心包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;javax.servlet-api&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;3.0.1&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;javax.servlet.jsp&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;javax.servlet.jsp-api&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;2.3.2-b01&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- jstl --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;jstl&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.2&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 添加mysql驱动包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;mysql&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;5.1.34&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 添加druid连接池包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;com.alibaba&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;druid&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.0.12&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 日志文件管理包 --&gt;</span><br><span class="line">        &lt;!-- log start --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;log4j&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;log4j&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;log4j.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 格式化对象，方便输出日志 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;com.alibaba&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;fastjson&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.1.41&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;slf4j.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;slf4j.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- log end --&gt;</span><br><span class="line">        &lt;!--apache shiro  --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.apache.shiro&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;shiro-core&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.2.2&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.apache.shiro&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;shiro-web&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.2.2&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.apache.shiro&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;shiro-spring&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.2.2&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!--apache shiro  end--&gt;</span><br><span class="line">        &lt;!-- 映入JSON --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.codehaus.jackson&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;jackson-mapper-asl&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.9.13&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!-- 上传组件包 --&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;commons-fileupload&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;commons-fileupload&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.3.1&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;commons-io&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;commons-io&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;2.4&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;commons-codec&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;commons-codec&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.9&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.apache.velocity&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;velocity&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.7&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.apache.velocity&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;velocity-tools&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;2.0&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.mybatis.generator&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;mybatis-generator-core&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;1.3.2&lt;/version&gt;</span><br><span class="line">            &lt;scope&gt;test&lt;/scope&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.quartz-scheduler&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;quartz&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;quartz.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!--activity工作流依赖--&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.activiti&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;activiti-engine&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;activiti.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;!--activiti与Spring集成--&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.activiti&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;activiti-spring&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;$&#123;activiti.version&#125;&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">        &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;com.qiniu&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;qiniu-java-sdk&lt;/artifactId&gt;</span><br><span class="line">            &lt;version&gt;7.2.6&lt;/version&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">    &lt;/dependencies&gt;</span><br><span class="line">    &lt;build&gt;</span><br><span class="line">        &lt;finalName&gt;smallkk&lt;/finalName&gt;</span><br><span class="line">        &lt;!--配置强制加载  在启动编译的时候可能会编译有些xml文件;所以此处给它强制加载--&gt;</span><br><span class="line">        &lt;resources&gt;</span><br><span class="line">            &lt;resource&gt;</span><br><span class="line">                &lt;directory&gt;src/main/java&lt;/directory&gt;</span><br><span class="line">                &lt;includes&gt;</span><br><span class="line">                    &lt;include&gt;**/*.xml&lt;/include&gt;</span><br><span class="line">                &lt;/includes&gt;</span><br><span class="line">            &lt;/resource&gt;</span><br><span class="line">        &lt;/resources&gt;</span><br><span class="line">        &lt;plugins&gt;</span><br><span class="line">            &lt;plugin&gt;</span><br><span class="line">                &lt;groupId&gt;org.mybatis.generator&lt;/groupId&gt;</span><br><span class="line">                &lt;artifactId&gt;mybatis-generator-maven-plugin&lt;/artifactId&gt;</span><br><span class="line">                &lt;version&gt;1.3.2&lt;/version&gt;</span><br><span class="line">                &lt;configuration&gt;</span><br><span class="line">                    &lt;verbose&gt;true&lt;/verbose&gt;</span><br><span class="line">                    &lt;overwrite&gt;true&lt;/overwrite&gt;</span><br><span class="line">                &lt;/configuration&gt;</span><br><span class="line">            &lt;/plugin&gt;</span><br><span class="line">            &lt;plugin&gt;</span><br><span class="line">                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;</span><br><span class="line">                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;</span><br><span class="line">                &lt;version&gt;2.3.2&lt;/version&gt;</span><br><span class="line">                &lt;configuration&gt;</span><br><span class="line">                    &lt;source&gt;1.8&lt;/source&gt;</span><br><span class="line">                    &lt;target&gt;1.8&lt;/target&gt;</span><br><span class="line">                &lt;/configuration&gt;</span><br><span class="line">            &lt;/plugin&gt;</span><br><span class="line">        &lt;/plugins&gt;</span><br><span class="line">    &lt;/build&gt;</span><br><span class="line">&lt;/project&gt;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">/**************************************** log4j *******************************************/</span><br><span class="line">######################################</span><br><span class="line">#         log4j配置相关说明</span><br><span class="line">######################################</span><br><span class="line">#%p 输出优先级，即DEBUG，INFO，WARN，ERROR，FATAL</span><br><span class="line">#%r 输出自应用启动到输出该log信息耗费的毫秒数</span><br><span class="line">#%c 输出所属的类目，通常就是所在类的全名</span><br><span class="line">#%t 输出产生该日志事件的线程名</span><br><span class="line">#%m 输出代码中指定的信息</span><br><span class="line">#%n 输出一个回车换行符，Windows平台为“\r\n”，Unix平台为“\n”</span><br><span class="line">#%d 输出日志时间点的日期或时间，默认格式为ISO8601，也可以在其后指定格式，比如：%d&#123;yyyy MM dd HH:mm:ss,SSS&#125;，输出类似： 2002年10月18日 22：10：28，921</span><br><span class="line">#%l 输出日志事件的发生位置，包括类目名、发生的线程，以及在代码中的行数。举例：Testlog4.main(TestLog4.java:10)</span><br><span class="line">#log4j提供4种布局:</span><br><span class="line">#org.apache.log4j.HTMLLayout（以HTML表格形式布局）</span><br><span class="line">#org.apache.log4j.PatternLayout（可以灵活地指定布局模式），</span><br><span class="line">#org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），</span><br><span class="line">#org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息</span><br><span class="line">#log4j中有五级logger 输出级别:</span><br><span class="line">#FATAL 0</span><br><span class="line">#ERROR 3</span><br><span class="line">#WARN 4</span><br><span class="line">#INFO 6</span><br><span class="line">#DEBUG 7</span><br><span class="line">######################################</span><br><span class="line">#          log4j相关配置</span><br><span class="line">######################################</span><br><span class="line">#日志输出级别</span><br><span class="line">log4j.rootLogger=DEBUG,stdout,other</span><br><span class="line">#设置stdout的日志输出控制台</span><br><span class="line">log4j.appender.stdout=org.apache.log4j.ConsoleAppender</span><br><span class="line">#输出日志到控制台的方式，默认为System.out</span><br><span class="line">log4j.appender.stdout.Target=System.out</span><br><span class="line">#设置使用灵活布局</span><br><span class="line">log4j.appender.stdout.layout=org.apache.log4j.PatternLayout</span><br><span class="line">#灵活定义输出格式</span><br><span class="line">log4j.appender.stdout.layout.ConversionPattern=[%p][%d&#123;yyyy-MM-dd HH:mm:ss&#125;] %l %m %n</span><br><span class="line">#设置other的日志输出控制台</span><br><span class="line">log4j.appender.other=org.apache.log4j.RollingFileAppender</span><br><span class="line">#设置other的输出日志</span><br><span class="line">log4j.appender.other.File=/work-attend/logs/log.txt</span><br><span class="line">#设置other的日志最大限制</span><br><span class="line">log4j.appender.other.MaxFileSize=1024KB</span><br><span class="line">#最多只保存20个备份文件</span><br><span class="line">log4j.appender.other.MaxBackupIndex=1000  </span><br><span class="line">#输出INFO级别以上的日志</span><br><span class="line">log4j.appender.other.Threshold=INFO</span><br><span class="line">#设置使用灵活布局</span><br><span class="line">log4j.appender.other.layout=org.apache.log4j.PatternLayout  </span><br><span class="line">#灵活定义输出格式</span><br><span class="line">log4j.appender.other.layout.ConversionPattern=[%p][%d&#123;yyyy-MM-dd HH:mm:ss&#125;] %l %t %m %n    </span><br><span class="line">###显示SQL语句部分</span><br><span class="line">log4j.logger.com.mybatis=DEBUG</span><br><span class="line">log4j.logger.com.mybatis.common.jdbc.SimpleDataSource=DEBUG</span><br><span class="line">log4j.logger.com.mybatis.common.jdbc.ScriptRunner=DEBUG</span><br><span class="line">log4j.logger.com.mybatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG</span><br><span class="line">log4j.logger.java.sql.Connection=DEBUG</span><br><span class="line">log4j.logger.java.sql.Statement=DEBUG</span><br><span class="line">log4j.logger.java.sql.PreparedStatement=DEBUG</span><br><span class="line">log4j.logger.java.sql.ResultSet=DEBUG</span><br><span class="line">/********************************** logback.xml **************************************/</span><br><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br><span class="line">&lt;configuration&gt;</span><br><span class="line">    &lt;property name=&quot;LOG_HOME&quot; value=&quot;/logs/RecoverMaMaBike/&quot;/&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!--主要输出配置项目--&gt;</span><br><span class="line">    &lt;!--输出配置标签： 控制台输出--&gt;</span><br><span class="line">    &lt;appender name=&quot;Console&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&gt;</span><br><span class="line">        &lt;encoder&gt;</span><br><span class="line">            &lt;!--输出的格式--&gt;</span><br><span class="line">            &lt;pattern&gt;%d&#123;H:mm&#125; %-5level [%logger&#123;16&#125;] %msg%n&lt;/pattern&gt;</span><br><span class="line">        &lt;/encoder&gt;</span><br><span class="line">    &lt;/appender&gt;</span><br><span class="line">    &lt;!--正常日志配置--&gt;</span><br><span class="line">    &lt;appender name=&quot;normalLog&quot;</span><br><span class="line">              class=&quot;ch.qos.logback.core.rolling.RollingFileAppender&quot;&gt;</span><br><span class="line">        &lt;!--滚动策略 基于时间来滚动(每天生成一个文本)--&gt;</span><br><span class="line">        &lt;rollingPolicy class=&quot;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&quot;&gt;</span><br><span class="line">            &lt;!--文件名字格式--&gt;</span><br><span class="line">            &lt;FileNamePattern&gt;$&#123;LOG_HOME&#125;/web.normal.%d&#123;yyyy-MM-dd&#125;.log</span><br><span class="line">            &lt;/FileNamePattern&gt;</span><br><span class="line">            &lt;MaxHistory&gt;30&lt;/MaxHistory&gt;</span><br><span class="line">        &lt;/rollingPolicy&gt;</span><br><span class="line">        &lt;!--超过10M的话，就格外生成一个新的文本(文件太大 电脑不好打开 不方便阅读)--&gt;</span><br><span class="line">        &lt;triggeringPolicy class=&quot;ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy&quot;&gt;</span><br><span class="line">            &lt;maxFileSize&gt;10MB&lt;/maxFileSize&gt;</span><br><span class="line">        &lt;/triggeringPolicy&gt;</span><br><span class="line">        &lt;!--日志内容格式--&gt;</span><br><span class="line">        &lt;layout class=&quot;ch.qos.logback.classic.PatternLayout&quot;&gt;</span><br><span class="line">            &lt;pattern&gt;%d&#123;HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;16&#125; - %msg%n</span><br><span class="line">            &lt;/pattern&gt;</span><br><span class="line">        &lt;/layout&gt;</span><br><span class="line">        &lt;!--重点配置--&gt;</span><br><span class="line">        &lt;!--重点配置--&gt;</span><br><span class="line">        &lt;!--重点配置--&gt;</span><br><span class="line">        &lt;filter class=&quot;ch.qos.logback.classic.filter.LevelFilter&quot;&gt;</span><br><span class="line">            &lt;level&gt;ERROR&lt;/level&gt;</span><br><span class="line">            &lt;!--上下两句的意思是： 如果检测出错误(上句)就拒绝写入输出(下句);如果没有匹配带错误,就写书--&gt;</span><br><span class="line">            &lt;onMatch&gt;DENY&lt;/onMatch&gt;</span><br><span class="line">            &lt;onMismatch&gt;ACCEPT&lt;/onMismatch&gt;</span><br><span class="line">        &lt;/filter&gt;</span><br><span class="line">    &lt;/appender&gt;</span><br><span class="line"></span><br><span class="line">    &lt;appender name=&quot;errorLog&quot;</span><br><span class="line">              class=&quot;ch.qos.logback.core.rolling.RollingFileAppender&quot;&gt;</span><br><span class="line">        &lt;rollingPolicy class=&quot;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&quot;&gt;</span><br><span class="line">            &lt;FileNamePattern&gt;$&#123;LOG_HOME&#125;/web.error.%d&#123;yyyy-MM-dd&#125;.log</span><br><span class="line">            &lt;/FileNamePattern&gt;</span><br><span class="line">            &lt;MaxHistory&gt;30&lt;/MaxHistory&gt;</span><br><span class="line">        &lt;/rollingPolicy&gt;</span><br><span class="line">        &lt;triggeringPolicy class=&quot;ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy&quot;&gt;</span><br><span class="line">            &lt;maxFileSize&gt;10MB&lt;/maxFileSize&gt;</span><br><span class="line">        &lt;/triggeringPolicy&gt;</span><br><span class="line">        &lt;layout class=&quot;ch.qos.logback.classic.PatternLayout&quot;&gt;</span><br><span class="line">            &lt;pattern&gt;%d&#123;HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;16&#125; - %msg%n</span><br><span class="line">            &lt;/pattern&gt;</span><br><span class="line">        &lt;/layout&gt;</span><br><span class="line">        &lt;filter class=&quot;ch.qos.logback.classic.filter.LevelFilter&quot;&gt;</span><br><span class="line">            &lt;level&gt;ERROR&lt;/level&gt;</span><br><span class="line">            &lt;onMatch&gt;ACCEPT&lt;/onMatch&gt;</span><br><span class="line">            &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;</span><br><span class="line">        &lt;/filter&gt;</span><br><span class="line">    &lt;/appender&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!--表示 com.coder520.recovermamabike 这个目录下的所有输出全部放到 normalLog errorLog 这两个分类里面--&gt;</span><br><span class="line">    &lt;logger name=&quot;com.smallkk.recovermamabike&quot; level=&quot;debug&quot;&gt;</span><br><span class="line">        &lt;appender-ref ref=&quot;normalLog&quot;/&gt;</span><br><span class="line">        &lt;appender-ref ref=&quot;errorLog&quot;/&gt;</span><br><span class="line">    &lt;/logger&gt;</span><br><span class="line">    &lt;!--PS：此处可以分DAO层，Controller层，分各种层来分别输出--&gt;</span><br><span class="line"></span><br><span class="line">    &lt;root level=&quot;info&quot;&gt;</span><br><span class="line">        &lt;appender-ref ref=&quot;Console&quot;/&gt;</span><br><span class="line">    &lt;/root&gt;</span><br><span class="line">&lt;/configuration&gt;</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://smallkk.gitee.io/2018/02/28/RecoverMaMaBike/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="S-Name Small·kk">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Hi , 小坤坤">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/2018/02/28/RecoverMaMaBike/" itemprop="url">PublicBike</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-02-28T09:30:52+08:00">
                2018-02-28
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h6 id="1-springboot与SSM的关系："><a href="#1-springboot与SSM的关系：" class="headerlink" title="1. springboot与SSM的关系："></a>1. springboot与SSM的关系：</h6><ol>
<li><p>SSM需要配置Tomcat打包是个war包，war包需要在一定的环境下才能够运行；</p>
</li>
<li><p>springboot是内置了Tomcat(内部集成，从主函数里面启动)打包后是jar包；springboot配置简化，没有SSM那么麻烦</p>
</li>
<li><p>为什么做后台app用Springboot比较方便，是因为springboot是jar包，在运行的时候不需要tomcat,只需要有java环境就够了</p>
</li>
<li><p>微服务，分布式，主流的实现方式是用Springboot</p>
</li>
<li>如果是管理系统等需要用页面来展示的话，那么springboot对JSP，velocity的支持不是很好</li>
<li>RestController 是 spring4.x才有的，整个Controller下都是JSON，不需要像之前那样每个方法都要有@Res..body</li>
<li>SpringBoot里面开始事务：在主函数里面加上注解：@EnableTransactionManagement</li>
</ol>
<h6 id="2-Others"><a href="#2-Others" class="headerlink" title="2. Others:"></a>2. Others:</h6><p>==注意点==：<br>平常Java开发里面的“数据源”是直接写在Java代码中或者配置文件中的开发人员可以知道但是不利于隐私；而“JNDI”的数据源是写在Tomcat里面的，是封装好了的，此时开发人员不会知道数据源怎么配置的和配置在哪里的</p>
<p>JNDI (Java Naming and Directory Interface,Java命名和目录接口)</p>
<p>在Springboot里面，开发账号都是低权限的账号而且是只读状态，允许调试不允许修改；发布账号(高级账号)一般都在运维人员或者组长项目经理那里才知道</p>
<p>配置文件：perproties写起来太麻烦，所以用yml有层级公用感觉,如果有情况需要用到的有序配置的话需要.ini</p>
<h5 id="3-测试"><a href="#3-测试" class="headerlink" title="3. 测试"></a>3. 测试</h5><p>SpringBoot测试类的配置：</p>
<pre><code>@RunWith(SpringRunner.class)
@SpringBootTest(classes = RecovermamabikeApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//第一个参数：运行类是哪个类，也就是入口从哪儿开始；
//后面的两个参数是-这是一个web项目，需要springmvc的支持，即webEnvironment;
//后面的RANDOM_PORT表示下面的那个端口随机
</code></pre><p>在测试类里面需要添加的两个东西：</p>
<pre><code>@Autowired
private TestRestTemplate restTemplate;
//主要是模拟请求：测试controller层要返回JSON数据的话就要这个东西
@LocalServerPort
private int port;
//在web容器中需要监听一个端口，模拟一个请求过去看看测试通过不通过
//如果是普通测试，就如上一个类配置中有个“.RANDOM_PORT”
//如果是要跑服务器的话就需要指定一个特定的端口
</code></pre><p>写测试方法：</p>
<pre><code> @Test
public void contextLoads() {
    String result = restTemplate.getForObject(&quot;/user/hello&quot;, String.class);
    //controller层下面这个/user/hello/路径对应的方法返回的是一个String,所以此处的返回类型就是String；
    //用restTemplate去模拟一个请求得到那个方法返回；
    System.out.println(result);
    //在控制台输出测试内容的结果
}
</code></pre><h5 id="4-springBoot中数据处理："><a href="#4-springBoot中数据处理：" class="headerlink" title="4. springBoot中数据处理："></a>4. springBoot中数据处理：</h5><p>在springBoot中关于JSON传值类型的处理：<br>因为在SpringBoot里面是内置了”jackson”,但是jackson并没有fastjson那么爽；<br>在SSM配置中配置fastjson是在spring-mvc.xml里面配置了一个消息转换器：MessageConvert</p>
<pre><code>fastjson与jackson的区别：
1. jackson会将数据库里字段为“null”的也转换了特别影响数据的转换而且为null的也没有用；
2.  fastjson就改进了这个，不会将为空的字段也转换！
</code></pre><p>如何将jackson转换成fastjson：</p>
<p>   需要声明一个@Bean去覆盖，声明到任何地方都可以，但是像springboot这种框架集成的，一般都是声明在主函数之中的：</p>
<pre><code>/**
  * Author song  醉美柳舞之众星捧月
  * Date &amp; Time  2018/1/15 13:22
  * Description  fastjson 覆盖 jackjson 的消息转换器  =====&gt;&gt;&gt;  数据库中的字段为null就不会转换过来
  */
 @Bean
 public HttpMessageConverters fastJsonHttpMessageConverters() {
     FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
     HttpMessageConverter&lt;?&gt; converter = fastConverter;
     return new HttpMessageConverters(converter);
 }
 //将这个@Bean放在springboot的主函数中
 //方法名称可以随便写，其他的都是固定的
 //声明一个@Bean是让spring能够扫描到
 //返回的时候，返回的这个新的东西会将原来返回的东西
</code></pre><h6 id="5-日志整合"><a href="#5-日志整合" class="headerlink" title="5. 日志整合"></a>5. 日志整合</h6><p>log4j : 将所有类别的信息都可以输出在文本(混乱，不能分类)</p>
<p>logback：能够清晰的分别输出不同类型的日志在不同的文件中</p>
<pre><code>     application.yml配置：
      logging:
  config: classpath:logback.xml


    // logback的两个依赖:(同类东西 版本号必须要一致 )
     &lt;dependency&gt;
        &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
        &lt;artifactId&gt;logback-core&lt;/artifactId&gt;
        &lt;version&gt;1.2.3&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
        &lt;artifactId&gt;logback-classic&lt;/artifactId&gt;
        &lt;version&gt;1.2.3&lt;/version&gt;
        &lt;scope&gt;test&lt;/scope&gt;
    &lt;/dependency&gt;
     &lt;dependency&gt;
        &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
        &lt;artifactId&gt;logback-access&lt;/artifactId&gt;
        &lt;version&gt;1.1.3&lt;/version&gt;
    &lt;/dependency&gt;
ps: logback的日志需要人为写(主动记录)，才会写进错误日志里：
    人为写的动作为： log.error(&quot;ErroeMessage:&quot;,e);
</code></pre><p>springBoot中可能，只是可能不会读取“resource”文件夹里面的xml配置文件等，那么需要：</p>
<pre><code>&lt;resource&gt;
    &lt;directory&gt;${basedir}/src/main/resources&lt;/directory&gt;
    &lt;includes&gt;
        &lt;!--&lt;include&gt;contexts/&lt;/include&gt;--&gt;
        &lt;!--&lt;include&gt;*.xml&lt;/include&gt;--&gt;
        &lt;include&gt;*.yml&lt;/include&gt;
        &lt;include&gt;*.properties&lt;/include&gt;
        &lt;include&gt;*.xml&lt;/include&gt;
        &lt;include&gt;enc_pri&lt;/include&gt;
    &lt;/includes&gt;
&lt;/resource&gt;
</code></pre><p>注入异常之一：</p>
<pre><code>@Autowared
privite UserService userService;
//有的时候注入启动查看debug可以看到，并不是自己想的这个userService
//并且还找不到userService里面的方法，那么此时：

  @Autowared
  @Qualifiter(&quot;***.Impl&quot;)
 privite UserService userService;
//  @Qualifiter 表示这个Mapper注入哪一个实现类才合格
</code></pre><p>==用户登录==：</p>
<h5 id="6-session一致性问题："><a href="#6-session一致性问题：" class="headerlink" title="6. session一致性问题："></a>6. session一致性问题：</h5><p>没有浏览器，是安卓端，没有cookie概念那么session那么存活？(session是基于cookie的)</p>
<pre><code>基于session这样的东西，是有状态的，有标志性的,
是一个有状态的请求的statefull;
然而安卓app端的session是无状态的
</code></pre><p>因此采用session外置：Nginx轮询到不同的服务器上都可以访问到这个外置的session</p>
<p>session不能局限在Tomcat，只要能够标识一个会话的就可以当做是一个session,唯一识别用户身份的什么东西码都可以叫做session</p>
<p>用户登录的时候生成一个token(自定义的，相当于session)，将这个session存进redis，设置超时，用户每次请求 都要带着token 服务端用来做校验</p>
<h5 id="7-About-JSON"><a href="#7-About-JSON" class="headerlink" title="7. About JSON"></a>7. About JSON</h5><p>这个方法接收的参数是JSON</p>
<pre><code>public ApiResult&lt;String&gt; login(@RequestBody LoginInfo loginInfo) {
</code></pre><p>将JSON转化为字符串：</p>
<pre><code>JSONObject jsonObject = JSON.parseObject(decryptData);
            String mobile = jsonObject.getString(&quot;mobile&quot;);
            String verifycode = jsonObject.getString(&quot;code&quot;);
</code></pre><h5 id="8-从文件配置中取值"><a href="#8-从文件配置中取值" class="headerlink" title="8. 从文件配置中取值"></a>8. 从文件配置中取值</h5><p>①:在resource资源文件中的配置值：</p>
<pre><code>    redis:
host: localhost
port: 6379
auth:
max-idle: 5
max-total: 10
max-wait-millis: 3000
</code></pre><p>②:声明到代码并@Component让spring扫描到</p>
<pre><code>@Component
public class Parameters {
    /** redis config start */
    @Value(&quot;${redis.host}&quot;)
    private String redisHost;
    @Value(&quot;${redis.port}&quot;)
    private int redisPort;
    @Value(&quot;${redis.max-total}&quot;)
    private int redisMaxTotal;
    @Value(&quot;${redis.max-idle}&quot;)
    private int redisMaxIdle;
    @Value(&quot;${redis.max-wait-millis}&quot;)
    private int redisMaxWaitMillis;
    /** redis config end */
</code></pre><p>③:在需要值的地方取值</p>
<pre><code>JedisPoolConfig config = new JedisPoolConfig();
       config.setMaxWaitMillis(parameters.getRedisMaxWaitMillis());
       config.setMaxIdle(parameters.getRedisMaxIdle());
       config.setMaxTotal(parameters.getRedisMaxTotal());
       jedisPool = new JedisPool(config, parameters.getRedisHost(), parameters.getRedisPort(), 3000);
</code></pre><p>在本项目中的==AES与RSA==加密流程:</p>
<p>RSA生成密钥对(私钥，公钥)</p>
<p>AES加密： 明文 + AES的公钥 = 密文</p>
<p>RSA加密： 用 RSA的公钥去加密AES的公钥</p>
<p>RSA解密： 用 RSA的私钥去解密上面加密的”AES的公钥”的结果</p>
<p>AES解密： 用RSA解密的结果 + 密文 = 明文</p>
<p>RSA 基于 大因式分解 </p>
<p>HTTP 不安全 传输过程 完全暴露</p>
<ul>
<li style="list-style: none"><input type="checkbox" checked> 单车加密：AES对称加密明文：  明文 + 密钥 = 密文；      安卓端的密钥是根据用户名等信息提供生成(随机)</li>
</ul>
<p>传输过程：用RSA非对称加密去加密AES对称的密钥 ，jiamiMethod(AES的密钥)； (RSA对称加密的公钥和私钥看看能不能根据用户的信息也随机生成)</p>
<p>然后安卓端的代码，再代码混淆，不会将存在安卓端本地的AES公钥给反编译出来</p>
<h6 id="9-代码一定执行"><a href="#9-代码一定执行" class="headerlink" title="9. 代码一定执行"></a>9. 代码一定执行</h6><p>代码中某一个方法，并不会被调用但是希望这个方法能够执行：</p>
<p>引入注解：@PostConstruct;作用效果与静态代码块的功能一致：</p>
<pre><code>   /**
     * 初始化连接池
     */
    @PostConstruct
    public void init() throws MaMaBikeException {
        try {
// 这个方法就算不被调用，但是一定会执行！ 
</code></pre><p>返回主键:</p>
<pre><code>useGeneratedKeys=&quot;true&quot;
keyProperty=&quot;id&quot;
</code></pre><p>判断是否为空:</p>
<pre><code>①: StringUtils.isBlank() 
//comons.lang3 元素
②: CollectionUtils.isEmpty()   
//CollectionUtils 集合
</code></pre><p>文件占位符解析</p>
<pre><code>/**
 * 用于properties文件占位符解析
 * @return
 * @Value(&quot;#{&apos;${security.noneSecurityPath}&apos;.split(&apos;,&apos;)}&quot;) 作用是识别上面的&quot;#&quot;&amp;&quot;split&quot;
 */
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
    return new PropertySourcesPlaceholderConfigurer();
}
</code></pre><h5 id="10-SpringSecurity"><a href="#10-SpringSecurity" class="headerlink" title="==10==. SpringSecurity"></a>==10==. SpringSecurity</h5><hr>
<p>授权 与 预授权</p>
<p>授权: 在本系统内部加载权限信息</p>
<p>预授权: 在别的系统 或者别的代码中 已经进行过授权</p>
<p>pre manager —- provider (一个或N个 提供授权信息) entrypoint 统一异常处理</p>
<p>SpringSecurity类配置：</p>
<pre><code>@Configurable
@EnableWebSecurity
@EnableGlobalMethodSecurity
//@Configurable是能够让spring扫描到
//@EnableWebSecurity是开启SpringSecurity在web中的应用
//@EnableGlobalMethodSecurity在方法上面打注解进行权限拦截
public class SecurityConfig {
}
</code></pre><p>csrf 是一种 跨站脚本攻击 相当于伪造表单 类似钓鱼网站这种 复制网站表单欺骗用户 (将网站设置成唯一性验证就好,别人就复制不到了,要用到html里面的隐藏域由后端来生成数据)</p>
<p>将集合转为String数组：</p>
<pre><code>parameters.getNoneSecurityPath().toArray(new String[parameters.getNoneSecurityPath().size()]
//下面是将数组转成集合
Arrays.asList(authorities)
</code></pre><p>Spring的路径匹配器：</p>
<pre><code>private AntPathMatcher matcher = new AntPathMatcher();
</code></pre><p>本次流程：</p>
<ul>
<li style="list-style: none"><input type="checkbox" checked> ①：发送请求后先到<code>RestPreAuthenticatedProcessingFilter</code>类里的<code>getPreAuthenticatedPrincipal</code>方法获取用户信息(检查是否需要授权,如果是登录则给任意权限进入,如果是用车或者修改信息则给单车用户权限,与此同时校验传入的版本号和Token并返回session错误信息)</li>
<li style="list-style: none"><input type="checkbox" checked> ②：进入到<code>RestAuthenticationProvider</code>类里面的<code>supports</code>方法判断从上面(RestPreAuthenticatedProcessingFilter类)传过来的Object是不是想要的东西,如果是则返回true</li>
<li style="list-style: none"><input type="checkbox" checked> ③：进入到<code>RestAuthenticationProvider</code>类里面的<code>authenticate</code>方法进行校验从上面(RestPreAuthenticatedProcessingFilter类)传递过来的授权的角色判断此处应该返回什么信息,如果有这个权限的角色则返回验证通过反之,抛出异常</li>
<li style="list-style: none"><input type="checkbox" checked> ④：如果上面(③)返回的是通过的话,此时应该进入路径请求的Controller层方法进行业务逻辑代码</li>
<li style="list-style: none"><input type="checkbox" checked> ⑤：若整个流程出现问题或者上面(③)抛出异常的话,此时会进入到<code>RestAuthenticationEntryPoint</code>类里面的<code>commence</code>的方法进行统一异常处理;根据前面(①)提到的session错误进行相应的返回并且设置跨域请求！</li>
<li style="list-style: none"><input type="checkbox" checked> ###### SpringSecurity End</li>
</ul>
<p>跨域设置：</p>
<pre><code>try {
           //设置跨域请求 请求结果json刷到响应里 前后端分离的时候都要这么设置
           // 这种设置是  后台端 与 前端或者安卓端 不在一块内存中 或者不在同一台服务器中
           httpServletResponse.setHeader(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
           httpServletResponse.setHeader(&quot;Access-Control-Allow-Methods&quot;, &quot;POST,GET,OPTIONS,DELETE&quot;);
           httpServletResponse.setHeader(&quot;Access-Control-Max-Age&quot;, &quot;3600&quot;);
           httpServletResponse.setHeader(&quot;Access-Control-Allow-Headers&quot;, &quot;Z-Requested-With,
           user-token,Content-Type,Accept,version,type,platform&quot;);
           httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
           // 这上面的 5行  都是强制代码 ;固然要这么做
           httpServletResponse.getWriter().write(JSON.toJSONString(result));
           //上面这一行 是将上面的result写入即返回
           httpServletResponse.flushBuffer();
       } catch (Exception er) {
           log.error(&quot;Fail to send 401 httpServletResponse&quot;, er.getMessage());
       }
</code></pre><h5 id="11-短信"><a href="#11-短信" class="headerlink" title="11. 短信"></a>11. 短信</h5><ul>
<li><p>[x] 短信是http接口，需要http请求;平常用到的是postman来做http请求;在代码中 Http client 这个jar包来做辅助,模拟POST/GET请求</p>
</li>
<li><p>获取IP地址(客户端真正的ip地址,不会随Nginx转发而改变的原始ip)</p>
<pre><code>protected String getIpFromRequest(HttpServletRequest request) {
    String ip = request.getHeader(&quot;x-forwarded-for&quot;);
    if (ip == null || ip.length() == 0 || &quot;unknown&quot;.equalsIgnoreCase(ip)) {
        ip = request.getHeader(&quot;Proxy-Client-IP&quot;);
    }
    if (ip == null || ip.length() == 0 || &quot;unknown&quot;.equalsIgnoreCase(ip)) {
        ip = request.getHeader(&quot;WL-Proxy-Client-IP&quot;);
    }
    if (ip == null || ip.length() == 0 || &quot;unknown&quot;.equalsIgnoreCase(ip)) {
        ip = request.getRemoteAddr()
    }
    return ip.equalsIgnoreCase(&quot;0:0:0:0:0:0:1&quot;) ? &quot;127.0.0.1&quot; : ip;
}
</code></pre></li>
<li><p>[ ] 模拟POST请求：</p>
</li>
</ul>
<pre><code>public static String post(String url, Map&lt;String, String&gt; paramsMap) {
        // 创造一个HttpClients的客户端
        CloseableHttpClient client = HttpClients.createDefault();
        String responseText = &quot;&quot;;
        CloseableHttpResponse response = null;
        try {
            HttpPost method = new HttpPost(url);
            if (paramsMap != null) {
                List&lt;NameValuePair&gt; paramList = new ArrayList&lt;NameValuePair&gt;();
                for (Map.Entry&lt;String, String&gt; param : paramsMap.entrySet()) {
                    NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue());
                    paramList.add(pair);
                }
                method.setEntity(new UrlEncodedFormEntity(paramList, ENCODING));
            }
            response = client.execute(method);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                responseText = EntityUtils.toString(entity);
            }
        } catch (Exception e) {
            log.error(&quot;http request failed&quot;, e);
        } finally {
            try {
                response.close();
            } catch (Exception e) {
                log.error(&quot;&quot;, e);
            }
        }
        return responseText;
    }
</code></pre><p>短信发送类:</p>
<pre><code>@Component(&quot;smsProcessor&quot;)
public class SmsProcessor {
    /**
     * SmsProcessor
     * 既可以发送短信  又可以监听发送短信的队列去消费短信 去调用
     */
    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;
    //调用ActiveMQ的消息发送器
    @Autowired
    @Qualifier(&quot;verCodeService&quot;)
    private SmsSender sendSms;
    /**
     * 消息发送的方法
     */
    public void sendSmsToQueue(Destination destination, final String message) {
        //destination 表示短信发送的 名字在哪儿/目的地在哪儿
        jmsMessagingTemplate.convertAndSend(destination, message);
        // message 一般都是转成json传出去
    }
    //destination = &quot;sms.queue&quot; 表示目的是&quot;sms.queue&quot; 短信队列
    // 表示这个要被监听
    @JmsListener(destination = &quot;sms.queue&quot;)
    public void doSendSmsMessage(String text) {
        //text是收到的消息
        JSONObject jsonObject = JSON.parseObject(text);
        sendSms.sendSms(jsonObject.getString(&quot;mobile&quot;), jsonObject.getString(&quot;tplid&quot;), jsonObject.getString(&quot;vercode&quot;));
    }
}
</code></pre><h5 id="12-自行车"><a href="#12-自行车" class="headerlink" title="12. 自行车"></a>12. 自行车</h5><hr>
<p>查找附件单车  GPS美国的 全球定位系统 N多卫星 以及备用卫星 全球坐标系经纬度<br>             中国的北斗  欧洲的伽利略  俄罗斯的格洛纳斯<br>用户登录APP 上报经纬度 服务器查找附件的单车 返回单车的经纬度 安卓对接百度地图 把经纬度坐标点显示在地图上  图标可以显示</p>
<p>开锁： 改变单着的状态 让其他用户搜索不到这辆车  操作：设计推送：就是服务器和客户端沟通<br>骑行历史记录 用户一边骑车 车子要 不间断的上报坐标 服务器记录</p>
<p>服务器查找附件的单车 : 上传的坐标</p>
<p>geoHash (地理哈希值)计算地理; 支持geoHash的是NOSQL ， NOSQL对事务的支持很弱<br>mysql RDS 关系型数据库 强项 是 事务 ACID ：原子性（Atomicity）、一致性（Consistency）、隔离性（Isolation）、持久性（Durability）。</p>
<p>MongoDB ElasticSearch 全文检索的中间件 可以理解为数据库</p>
<p>MongoDB是存文档的数据库 BSON JSON</p>
<table>
<thead>
<tr>
<th>SQL术语</th>
<th>Mongodb术语</th>
</tr>
</thead>
<tbody>
<tr>
<td>数据库： database</td>
<td>数据库： database</td>
</tr>
<tr>
<td>数据库表: table</td>
<td>集合: collection</td>
</tr>
<tr>
<td>数据记录行： row</td>
<td>文档： document</td>
</tr>
<tr>
<td>数据字段： column</td>
<td>域：field</td>
</tr>
<tr>
<td>表链接：table joins</td>
<td>Mg不支持表链接</td>
</tr>
<tr>
<td>主键：primary key</td>
<td>Mg自动将_id设为主键：primary key</td>
</tr>
</tbody>
</table>
<p>启动命令: </p>
<pre><code>在 G:\Plugins\MongoDB\Server\3.4\bin 下cmd ：
mongod --dbpath G:\Plugins\MongoDB\mongoData
</code></pre><p>搜索：</p>
<pre><code>db.getCollection(&apos;bike-position&apos;).find({})
//括号中跟要搜索的东西,为空即为全部
</code></pre><p>ORM: spring data 是 ORM 即 从Java到数据库的实体映射</p>
<h5 id="13-骑行-解锁"><a href="#13-骑行-解锁" class="headerlink" title="13. 骑行 解锁"></a>13. 骑行 解锁</h5><ul>
<li style="list-style: none"><input type="checkbox" checked> RSA解密出现问题;Java端与安卓端加密方式有些不同即,在解密过后也会出现点问题(Java代码生成的公钥在Java代码下可以用但是在 安卓端会解析失败(可能是Java生成的公钥有分段空格));解决:Openssl加密方式生成的公钥是没有空格的,Java端和安卓端都是可以使用的(openssl是用命令生成的),但是通过这样的公钥后在安卓端经过加密的东西到了JAVA端解密又会失败(会报错,抛出异常 algid parse error , not a sequence..,百度报错代码进入stackOverflow网站给出的解决方案是要贴入一个第三方包),那么此时Java端就要引入一个新的东西,”第三方解密包”</li>
</ul>
<p>推送原理 socket长连接 Netty是NIO网络通信的框架搞长连接这块非常牛</p>
<p>两种第三方: 极光推送 百度云推送</p>
<p>当前共5张表;<br>bike、ride_fee、ride_record、user、wallet;此处还应该有微信一张表和支付宝一张表,再来分库分表</p>
<ul>
<li><p>涉及到金额的一般用Decimal or BigDecimal;</p>
<ul>
<li>钱之间的比较就是:<br>wallet.getRemainSun().compareTO(New BigDecimal(1)&lt;0)<ul>
<li>cost = fee.getFee().multiply(new BigDecimal((intMin / minUnit) + 1));</li>
</ul>
</li>
<li>//BigDecimal 算乘法 用 multiply</li>
</ul>
</li>
<li><p>[x] 在单车骑行代码实现中:涉及到了事务回滚机制<code>@Transactional(rollbackFor = Exception.class)</code>但是这个涉及到的数据源有Mysql和Mongodb两种数据库,要么同时成功要么同时失败;这样是非常困难的(经常就会出现 车已经锁了,但是计费并没有结束 此时就需要连续客服 Mongodb没有事务这个概念的)</p>
</li>
</ul>
<ul>
<li style="list-style: none"><input type="checkbox" checked> 单车还有页面后台端,管理界面</li>
<li style="list-style: none"><input type="checkbox" checked> 迭代开发的时候还没有开发app里面的排碳量什么的</li>
<li style="list-style: none"><input type="checkbox" checked> // 此处不能用 new Date().toString 不是因为过时,而是因为这个时间与Mongodb中的时间不相匹配,导致安卓端调用数据会异常</li>
<li><pre><code>rideRecord.setRecordNo(System.currentTimeMillis() + RandomNumberCoder.randomNo());
</code></pre></li>
<li><p>[x] 订单号:</p>
<pre><code>rideRecord.setRecordNo(System.currentTimeMillis() 
+ RandomNumberCoder.randomNo());
//这个并不严谨;当前只是在一定的范围类可控不重复就行;
//但是在超级大并发的情况下就得重新考虑
</code></pre></li>
<li><p>MongoDB在java代码中的SQL:</p>
<pre><code> Query query = Query.query(Criteria.where(&quot;bike_no&quot;).is(bikeLocation.getBikeNumber()));
 //查询
① Update update = Update.update(&quot;status&quot;, BIKE_LOCK).set(&quot;location.coordinates&quot;, bikeLocation.getCoordinates());
//push类似于堆栈那种   因为MongoDB里面的代码是BSON数组,一个坐标点一个数组经纬度坐标,push就是往最后一个数组后面叠加一个数组
② Update update = new Update().push(&quot;contrail&quot;, new BasicDBObject(&quot;loc&quot;, bikeLocation.getCoordinates()));
 //更新
 mongoTemplate.updateFirst(query, update, &quot;bike-position&quot;);
 //执行
</code></pre></li>
<li>解锁或者锁车规则：<ul>
<li>必须要有用户的Token验证和每个单车独一无二的Token验证;验证通过了才可以进入单车解锁或者锁车的接口</li>
</ul>
</li>
</ul>
<h5 id="14-骑行轨迹"><a href="#14-骑行轨迹" class="headerlink" title="14. 骑行轨迹"></a>14. 骑行轨迹</h5><p>卡尔曼滤波算法 百度地图补点</p>
<p>BasicDBObject 这个东西像BSON而且类似Map,有Key与Value之分</p>
<pre><code>BasicDBObject obj = new BasicDBObject(&quot;key&quot;,value);
</code></pre><h5 id="15-Swagger"><a href="#15-Swagger" class="headerlink" title="15. Swagger"></a>15. Swagger</h5><ol>
<li><p>jar</p>
<pre><code>&lt;dependency&gt;
        &lt;groupId&gt;io.springfox&lt;/groupId&gt;
        &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
        &lt;version&gt;2.6.1&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;io.springfox&lt;/groupId&gt;
        &lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
        &lt;version&gt;2.6.1&lt;/version&gt;
    &lt;/dependency&gt;
</code></pre></li>
<li>与Fastjson冲突解决:(如果会有冲突的话)<ul>
<li>新建消息转换</li>
</ul>
</li>
</ol>
<pre><code>public class FastJsonHttpMessageConverterEx extends FastJsonHttpMessageConverter {
    public FastJsonHttpMessageConverterEx() {
        super();
        this.getFastJsonConfig().getSerializeConfig().put(Json.class, SwaggerJsonSerializer.instance);
    }
}
</code></pre><ul>
<li><p>覆盖(在启动类中)</p>
<pre><code>@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
    FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
    HttpMessageConverter&lt;?&gt; converter = fastConverter;
    return new HttpMessageConverters(converter);
}
</code></pre></li>
</ul>
<ol start="3">
<li>swagger 序列问题：</li>
</ol>
<pre><code>public class SwaggerJsonSerializer implements ObjectSerializer, ObjectDeserializer {

public final static SwaggerJsonSerializer instance = new SwaggerJsonSerializer();

@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
    SerializeWriter out = serializer.getWriter();
    Json json = (Json) object;
    out.write(json.value());
}

@Override
public &lt;T&gt; T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
    return null;
}

@Override
public int getFastMatchToken() {
    return 0;
}

}
//上面 2 中的SwaggerJsonSerializer.instance就是引用的此处的;
</code></pre><ol start="4">
<li>Swagger配置类</li>
</ol>
<ul>
<li style="list-style: none"><input type="checkbox" checked> 注意<code> .apis(RequestHandlerSelectors.any())</code>一定是<code>.any</code>不能是根当前的包名</li>
<li style="list-style: none"><input type="checkbox" checked> 注意<code>.paths(Predicates.not(PathSelectors.regex(“/error.*”)))</code>是必须要有的</li>
</ul>
<pre><code>       @Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.any())
               .paths(Predicates.not(PathSelectors.regex(&quot;/error.*&quot;)))
                .build();
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(&quot;共享单车联调接口(复习)&quot;)
                .contact(new Contact(&quot;宋贤坤&quot;, &quot;www.smallkk.com&quot;, &quot;songxiankun2008@126.com&quot;))
                .version(&quot;版本信息 Version-2.0&quot;)
                .license(&quot;版权信息 License&quot;)
                .licenseUrl(&quot;www.smallkk.com&quot;)
                .build();
    }
}
</code></pre><ol start="5">
<li><p>Controller层的注解:eg</p>
<pre><code>@ApiOperation(value = &quot;解锁单车&quot;, notes = &quot;根据单车编号解锁单车&quot;, httpMethod = &quot;POST&quot;)
   @ApiImplicitParam(name = &quot;bike&quot;, value = &quot;单车编号&quot;, required = true, dataType = &quot;Bike&quot;)
   @RequestMapping(&quot;/unLockBike&quot;)
   public ApiResult unLockBike(@RequestBody Bike bike) throws MaMaBikeException {
</code></pre></li>
</ol>
<h5 id="16-终章"><a href="#16-终章" class="headerlink" title="16. 终章"></a>16. 终章</h5><ol>
<li>在单车位置信息中添加单车的具体状态（在地图展示的时候需要单车信息）</li>
<li>添加用户端接口查询单车状态（在开锁时需要轮训单车是否已经打开，因为开锁还涉及到单车端，应该是单车端真正解锁之后才算是解锁成功</li>
<li>添加获取用户信息接口（缺失）</li>
<li>添加用户钱包信息查询接口（缺失）</li>
<li>更改上报坐标接口，在上报坐标过程中如果已经锁车，则告诉客户端骑行结束（单车端锁车，客户端显示）</li>
<li>添加单车端接口获取单车状态（单车端使用不需要登录）</li>
<li>添加单车端接口锁定单车（单车端所用 不需要登录）</li>
<li>更改锁定单车接口，锁定单车是的位置信息将会更新到骑行记录中（锁定单车时上报的坐标是骑行记录中的最终坐标不能缺失）</li>
<li>MongoDB数据库中的时间与sqlList中时间戳不匹配导致找不到骑行单(Bug)</li>
</ol>
<h6 id="xx共享单车："><a href="#xx共享单车：" class="headerlink" title="xx共享单车："></a>xx共享单车：</h6><ul>
<li style="list-style: none"><input type="checkbox" checked> 此项目为时下最流行的共享单车实现，实现了周围单车定位，骑行轨迹等等功能。</li>
<li><p>[x] 技术上主要采用springboot、SSM、Mysql、springSecurity做安全校验、ActiveMQ消息队列、redis管理session、Nginx负载均衡、对接百度地图、对接短信、图片云存储。采用MongoDB做地理围栏服务，查找附近单车。</p>
</li>
<li><p>[ ] ActiveMQ/redis/Nginx问答在各自视频里面；</p>
</li>
<li style="list-style: none"><input type="checkbox"> ==GeoHash== 将全球地理分为36块，分的越细 精度越高，对每一个区域都划分了不同的经纬度;Base32算法计算距离的远近;MongoDB支持GeoHash所以就可以查询哪个区域之间的距离</li>
</ul>
<p>项目周期半年；2个后台，1个产品经理，1个美工，1个安卓，1个IOS，1个测试，1个运维 (敏捷开发 创业公司)</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
  </section>

  
  <nav class="pagination">
    <span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="extend next" rel="next" href="/page/2/"><i class="fa fa-angle-right"></i></a>
  </nav>



          </div>
          


          

        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">S-Name Small·kk</p>
              <p class="site-description motion-element" itemprop="description">开源 | Java | 个人笔记 | 我的博客</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">12</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            

            

          </nav>

          

          

          
          

          
          

          

        </div>
      </section>

      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2018</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">S-Name Small·kk</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Pisces</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>



  

  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
